##// END OF EJS Templates
Docstring autoformatting....
Matthias Bussonnier -
Show More
@@ -1,1214 +1,1211 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
660 The following will display an interactive map of Mars with a point of
659 The following will display an interactive map of Mars with a point of
661 interest on frontend that do support GeoJSON display.
660 interest on frontend that do support GeoJSON display.
662
661
663 >>> from IPython.display import GeoJSON
662 >>> from IPython.display import GeoJSON
664
663
665 >>> GeoJSON(data={
664 >>> GeoJSON(data={
666 ... "type": "Feature",
665 ... "type": "Feature",
667 ... "geometry": {
666 ... "geometry": {
668 ... "type": "Point",
667 ... "type": "Point",
669 ... "coordinates": [-81.327, 296.038]
668 ... "coordinates": [-81.327, 296.038]
670 ... }
669 ... }
671 ... },
670 ... },
672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
671 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
673 ... layer_options={
672 ... layer_options={
674 ... "basemap_id": "celestia_mars-shaded-16k_global",
673 ... "basemap_id": "celestia_mars-shaded-16k_global",
675 ... "attribution" : "Celestia/praesepe",
674 ... "attribution" : "Celestia/praesepe",
676 ... "minZoom" : 0,
675 ... "minZoom" : 0,
677 ... "maxZoom" : 18,
676 ... "maxZoom" : 18,
678 ... })
677 ... })
679 <IPython.core.display.GeoJSON object>
678 <IPython.core.display.GeoJSON object>
680
679
681 In the terminal IPython, you will only see the text representation of
680 In the terminal IPython, you will only see the text representation of
682 the GeoJSON object.
681 the GeoJSON object.
683
682
684 """
683 """
685
684
686 super(GeoJSON, self).__init__(*args, **kwargs)
685 super(GeoJSON, self).__init__(*args, **kwargs)
687
686
688
687
689 def _ipython_display_(self):
688 def _ipython_display_(self):
690 bundle = {
689 bundle = {
691 'application/geo+json': self.data,
690 'application/geo+json': self.data,
692 'text/plain': '<IPython.display.GeoJSON object>'
691 'text/plain': '<IPython.display.GeoJSON object>'
693 }
692 }
694 metadata = {
693 metadata = {
695 'application/geo+json': self.metadata
694 'application/geo+json': self.metadata
696 }
695 }
697 display(bundle, metadata=metadata, raw=True)
696 display(bundle, metadata=metadata, raw=True)
698
697
699 class Javascript(TextDisplayObject):
698 class Javascript(TextDisplayObject):
700
699
701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
700 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
702 """Create a Javascript display object given raw data.
701 """Create a Javascript display object given raw data.
703
702
704 When this object is returned by an expression or passed to the
703 When this object is returned by an expression or passed to the
705 display function, it will result in the data being displayed
704 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
705 in the frontend. If the data is a URL, the data will first be
707 downloaded and then displayed.
706 downloaded and then displayed.
708
707
709 In the Notebook, the containing element will be available as `element`,
708 In the Notebook, the containing element will be available as `element`,
710 and jQuery will be available. Content appended to `element` will be
709 and jQuery will be available. Content appended to `element` will be
711 visible in the output area.
710 visible in the output area.
712
711
713 Parameters
712 Parameters
714 ----------
713 ----------
715 data : unicode, str or bytes
714 data : unicode, str or bytes
716 The Javascript source code or a URL to download it from.
715 The Javascript source code or a URL to download it from.
717 url : unicode
716 url : unicode
718 A URL to download the data from.
717 A URL to download the data from.
719 filename : unicode
718 filename : unicode
720 Path to a local file to load the data from.
719 Path to a local file to load the data from.
721 lib : list or str
720 lib : list or str
722 A sequence of Javascript library URLs to load asynchronously before
721 A sequence of Javascript library URLs to load asynchronously before
723 running the source code. The full URLs of the libraries should
722 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
723 be given. A single Javascript library URL can also be given as a
725 string.
724 string.
726 css: : list or str
725 css : list or str
727 A sequence of css files to load before running the source code.
726 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
727 The full URLs of the css files should be given. A single css URL
729 can also be given as a string.
728 can also be given as a string.
730 """
729 """
731 if isinstance(lib, str):
730 if isinstance(lib, str):
732 lib = [lib]
731 lib = [lib]
733 elif lib is None:
732 elif lib is None:
734 lib = []
733 lib = []
735 if isinstance(css, str):
734 if isinstance(css, str):
736 css = [css]
735 css = [css]
737 elif css is None:
736 elif css is None:
738 css = []
737 css = []
739 if not isinstance(lib, (list,tuple)):
738 if not isinstance(lib, (list,tuple)):
740 raise TypeError('expected sequence, got: %r' % lib)
739 raise TypeError('expected sequence, got: %r' % lib)
741 if not isinstance(css, (list,tuple)):
740 if not isinstance(css, (list,tuple)):
742 raise TypeError('expected sequence, got: %r' % css)
741 raise TypeError('expected sequence, got: %r' % css)
743 self.lib = lib
742 self.lib = lib
744 self.css = css
743 self.css = css
745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
744 super(Javascript, self).__init__(data=data, url=url, filename=filename)
746
745
747 def _repr_javascript_(self):
746 def _repr_javascript_(self):
748 r = ''
747 r = ''
749 for c in self.css:
748 for c in self.css:
750 r += _css_t % c
749 r += _css_t % c
751 for l in self.lib:
750 for l in self.lib:
752 r += _lib_t1 % l
751 r += _lib_t1 % l
753 r += self.data
752 r += self.data
754 r += _lib_t2*len(self.lib)
753 r += _lib_t2*len(self.lib)
755 return r
754 return r
756
755
757 # constants for identifying png/jpeg data
756 # constants for identifying png/jpeg data
758 _PNG = b'\x89PNG\r\n\x1a\n'
757 _PNG = b'\x89PNG\r\n\x1a\n'
759 _JPEG = b'\xff\xd8'
758 _JPEG = b'\xff\xd8'
760
759
761 def _pngxy(data):
760 def _pngxy(data):
762 """read the (width, height) from a PNG header"""
761 """read the (width, height) from a PNG header"""
763 ihdr = data.index(b'IHDR')
762 ihdr = data.index(b'IHDR')
764 # next 8 bytes are width/height
763 # next 8 bytes are width/height
765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
764 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
766
765
767 def _jpegxy(data):
766 def _jpegxy(data):
768 """read the (width, height) from a JPEG header"""
767 """read the (width, height) from a JPEG header"""
769 # adapted from http://www.64lines.com/jpeg-width-height
768 # adapted from http://www.64lines.com/jpeg-width-height
770
769
771 idx = 4
770 idx = 4
772 while True:
771 while True:
773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
772 block_size = struct.unpack('>H', data[idx:idx+2])[0]
774 idx = idx + block_size
773 idx = idx + block_size
775 if data[idx:idx+2] == b'\xFF\xC0':
774 if data[idx:idx+2] == b'\xFF\xC0':
776 # found Start of Frame
775 # found Start of Frame
777 iSOF = idx
776 iSOF = idx
778 break
777 break
779 else:
778 else:
780 # read another block
779 # read another block
781 idx += 2
780 idx += 2
782
781
783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
782 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
784 return w, h
783 return w, h
785
784
786 def _gifxy(data):
785 def _gifxy(data):
787 """read the (width, height) from a GIF header"""
786 """read the (width, height) from a GIF header"""
788 return struct.unpack('<HH', data[6:10])
787 return struct.unpack('<HH', data[6:10])
789
788
790
789
791 class Image(DisplayObject):
790 class Image(DisplayObject):
792
791
793 _read_flags = 'rb'
792 _read_flags = 'rb'
794 _FMT_JPEG = u'jpeg'
793 _FMT_JPEG = u'jpeg'
795 _FMT_PNG = u'png'
794 _FMT_PNG = u'png'
796 _FMT_GIF = u'gif'
795 _FMT_GIF = u'gif'
797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
796 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
798 _MIMETYPES = {
797 _MIMETYPES = {
799 _FMT_PNG: 'image/png',
798 _FMT_PNG: 'image/png',
800 _FMT_JPEG: 'image/jpeg',
799 _FMT_JPEG: 'image/jpeg',
801 _FMT_GIF: 'image/gif',
800 _FMT_GIF: 'image/gif',
802 }
801 }
803
802
804 def __init__(self, data=None, url=None, filename=None, format=None,
803 def __init__(self, data=None, url=None, filename=None, format=None,
805 embed=None, width=None, height=None, retina=False,
804 embed=None, width=None, height=None, retina=False,
806 unconfined=False, metadata=None):
805 unconfined=False, metadata=None):
807 """Create a PNG/JPEG/GIF image object given raw data.
806 """Create a PNG/JPEG/GIF image object given raw data.
808
807
809 When this object is returned by an input cell or passed to the
808 When this object is returned by an input cell or passed to the
810 display function, it will result in the image being displayed
809 display function, it will result in the image being displayed
811 in the frontend.
810 in the frontend.
812
811
813 Parameters
812 Parameters
814 ----------
813 ----------
815 data : unicode, str or bytes
814 data : unicode, str or bytes
816 The raw image data or a URL or filename to load the data from.
815 The raw image data or a URL or filename to load the data from.
817 This always results in embedded image data.
816 This always results in embedded image data.
818 url : unicode
817 url : unicode
819 A URL to download the data from. If you specify `url=`,
818 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`.
819 the image data will not be embedded unless you also specify `embed=True`.
821 filename : unicode
820 filename : unicode
822 Path to a local file to load the data from.
821 Path to a local file to load the data from.
823 Images from a file are always embedded.
822 Images from a file are always embedded.
824 format : unicode
823 format : unicode
825 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
824 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.
825 for format will be inferred from the filename extension.
827 embed : bool
826 embed : bool
828 Should the image data be embedded using a data URI (True) or be
827 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
828 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.
829 to be viewable later with no internet connection in the notebook.
831
830
832 Default is `True`, unless the keyword argument `url` is set, then
831 Default is `True`, unless the keyword argument `url` is set, then
833 default value is `False`.
832 default value is `False`.
834
833
835 Note that QtConsole is not able to display images if `embed` is set to `False`
834 Note that QtConsole is not able to display images if `embed` is set to `False`
836 width : int
835 width : int
837 Width in pixels to which to constrain the image in html
836 Width in pixels to which to constrain the image in html
838 height : int
837 height : int
839 Height in pixels to which to constrain the image in html
838 Height in pixels to which to constrain the image in html
840 retina : bool
839 retina : bool
841 Automatically set the width and height to half of the measured
840 Automatically set the width and height to half of the measured
842 width and height.
841 width and height.
843 This only works for embedded images because it reads the width/height
842 This only works for embedded images because it reads the width/height
844 from image data.
843 from image data.
845 For non-embedded images, you can just set the desired display width
844 For non-embedded images, you can just set the desired display width
846 and height directly.
845 and height directly.
847 unconfined: bool
846 unconfined : bool
848 Set unconfined=True to disable max-width confinement of the image.
847 Set unconfined=True to disable max-width confinement of the image.
849 metadata: dict
848 metadata : dict
850 Specify extra metadata to attach to the image.
849 Specify extra metadata to attach to the image.
851
850
852 Examples
851 Examples
853 --------
852 --------
854
855 embedded image data, works in qtconsole and notebook
853 embedded image data, works in qtconsole and notebook
856 when passed positionally, the first arg can be any of raw image data,
854 when passed positionally, the first arg can be any of raw image data,
857 a URL, or a filename from which to load image data.
855 a URL, or a filename from which to load image data.
858 The result is always embedding image data for inline images.
856 The result is always embedding image data for inline images.
859
857
860 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
858 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
861 <IPython.core.display.Image object>
859 <IPython.core.display.Image object>
862
860
863 >>> Image('/path/to/image.jpg')
861 >>> Image('/path/to/image.jpg')
864 <IPython.core.display.Image object>
862 <IPython.core.display.Image object>
865
863
866 >>> Image(b'RAW_PNG_DATA...')
864 >>> Image(b'RAW_PNG_DATA...')
867 <IPython.core.display.Image object>
865 <IPython.core.display.Image object>
868
866
869 Specifying Image(url=...) does not embed the image data,
867 Specifying Image(url=...) does not embed the image data,
870 it only generates ``<img>`` tag with a link to the source.
868 it only generates ``<img>`` tag with a link to the source.
871 This will not work in the qtconsole or offline.
869 This will not work in the qtconsole or offline.
872
870
873 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
871 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
872 <IPython.core.display.Image object>
874
873
875 """
874 """
876 if isinstance(data, (Path, PurePath)):
875 if isinstance(data, (Path, PurePath)):
877 data = str(data)
876 data = str(data)
878
877
879 if filename is not None:
878 if filename is not None:
880 ext = self._find_ext(filename)
879 ext = self._find_ext(filename)
881 elif url is not None:
880 elif url is not None:
882 ext = self._find_ext(url)
881 ext = self._find_ext(url)
883 elif data is None:
882 elif data is None:
884 raise ValueError("No image data found. Expecting filename, url, or data.")
883 raise ValueError("No image data found. Expecting filename, url, or data.")
885 elif isinstance(data, str) and (
884 elif isinstance(data, str) and (
886 data.startswith('http') or _safe_exists(data)
885 data.startswith('http') or _safe_exists(data)
887 ):
886 ):
888 ext = self._find_ext(data)
887 ext = self._find_ext(data)
889 else:
888 else:
890 ext = None
889 ext = None
891
890
892 if format is None:
891 if format is None:
893 if ext is not None:
892 if ext is not None:
894 if ext == u'jpg' or ext == u'jpeg':
893 if ext == u'jpg' or ext == u'jpeg':
895 format = self._FMT_JPEG
894 format = self._FMT_JPEG
896 elif ext == u'png':
895 elif ext == u'png':
897 format = self._FMT_PNG
896 format = self._FMT_PNG
898 elif ext == u'gif':
897 elif ext == u'gif':
899 format = self._FMT_GIF
898 format = self._FMT_GIF
900 else:
899 else:
901 format = ext.lower()
900 format = ext.lower()
902 elif isinstance(data, bytes):
901 elif isinstance(data, bytes):
903 # infer image type from image data header,
902 # infer image type from image data header,
904 # only if format has not been specified.
903 # only if format has not been specified.
905 if data[:2] == _JPEG:
904 if data[:2] == _JPEG:
906 format = self._FMT_JPEG
905 format = self._FMT_JPEG
907
906
908 # failed to detect format, default png
907 # failed to detect format, default png
909 if format is None:
908 if format is None:
910 format = self._FMT_PNG
909 format = self._FMT_PNG
911
910
912 if format.lower() == 'jpg':
911 if format.lower() == 'jpg':
913 # jpg->jpeg
912 # jpg->jpeg
914 format = self._FMT_JPEG
913 format = self._FMT_JPEG
915
914
916 self.format = format.lower()
915 self.format = format.lower()
917 self.embed = embed if embed is not None else (url is None)
916 self.embed = embed if embed is not None else (url is None)
918
917
919 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
918 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
920 raise ValueError("Cannot embed the '%s' image format" % (self.format))
919 raise ValueError("Cannot embed the '%s' image format" % (self.format))
921 if self.embed:
920 if self.embed:
922 self._mimetype = self._MIMETYPES.get(self.format)
921 self._mimetype = self._MIMETYPES.get(self.format)
923
922
924 self.width = width
923 self.width = width
925 self.height = height
924 self.height = height
926 self.retina = retina
925 self.retina = retina
927 self.unconfined = unconfined
926 self.unconfined = unconfined
928 super(Image, self).__init__(data=data, url=url, filename=filename,
927 super(Image, self).__init__(data=data, url=url, filename=filename,
929 metadata=metadata)
928 metadata=metadata)
930
929
931 if self.width is None and self.metadata.get('width', {}):
930 if self.width is None and self.metadata.get('width', {}):
932 self.width = metadata['width']
931 self.width = metadata['width']
933
932
934 if self.height is None and self.metadata.get('height', {}):
933 if self.height is None and self.metadata.get('height', {}):
935 self.height = metadata['height']
934 self.height = metadata['height']
936
935
937 if retina:
936 if retina:
938 self._retina_shape()
937 self._retina_shape()
939
938
940
939
941 def _retina_shape(self):
940 def _retina_shape(self):
942 """load pixel-doubled width and height from image data"""
941 """load pixel-doubled width and height from image data"""
943 if not self.embed:
942 if not self.embed:
944 return
943 return
945 if self.format == self._FMT_PNG:
944 if self.format == self._FMT_PNG:
946 w, h = _pngxy(self.data)
945 w, h = _pngxy(self.data)
947 elif self.format == self._FMT_JPEG:
946 elif self.format == self._FMT_JPEG:
948 w, h = _jpegxy(self.data)
947 w, h = _jpegxy(self.data)
949 elif self.format == self._FMT_GIF:
948 elif self.format == self._FMT_GIF:
950 w, h = _gifxy(self.data)
949 w, h = _gifxy(self.data)
951 else:
950 else:
952 # retina only supports png
951 # retina only supports png
953 return
952 return
954 self.width = w // 2
953 self.width = w // 2
955 self.height = h // 2
954 self.height = h // 2
956
955
957 def reload(self):
956 def reload(self):
958 """Reload the raw data from file or URL."""
957 """Reload the raw data from file or URL."""
959 if self.embed:
958 if self.embed:
960 super(Image,self).reload()
959 super(Image,self).reload()
961 if self.retina:
960 if self.retina:
962 self._retina_shape()
961 self._retina_shape()
963
962
964 def _repr_html_(self):
963 def _repr_html_(self):
965 if not self.embed:
964 if not self.embed:
966 width = height = klass = ''
965 width = height = klass = ''
967 if self.width:
966 if self.width:
968 width = ' width="%d"' % self.width
967 width = ' width="%d"' % self.width
969 if self.height:
968 if self.height:
970 height = ' height="%d"' % self.height
969 height = ' height="%d"' % self.height
971 if self.unconfined:
970 if self.unconfined:
972 klass = ' class="unconfined"'
971 klass = ' class="unconfined"'
973 return u'<img src="{url}"{width}{height}{klass}/>'.format(
972 return u'<img src="{url}"{width}{height}{klass}/>'.format(
974 url=self.url,
973 url=self.url,
975 width=width,
974 width=width,
976 height=height,
975 height=height,
977 klass=klass,
976 klass=klass,
978 )
977 )
979
978
980 def _repr_mimebundle_(self, include=None, exclude=None):
979 def _repr_mimebundle_(self, include=None, exclude=None):
981 """Return the image as a mimebundle
980 """Return the image as a mimebundle
982
981
983 Any new mimetype support should be implemented here.
982 Any new mimetype support should be implemented here.
984 """
983 """
985 if self.embed:
984 if self.embed:
986 mimetype = self._mimetype
985 mimetype = self._mimetype
987 data, metadata = self._data_and_metadata(always_both=True)
986 data, metadata = self._data_and_metadata(always_both=True)
988 if metadata:
987 if metadata:
989 metadata = {mimetype: metadata}
988 metadata = {mimetype: metadata}
990 return {mimetype: data}, metadata
989 return {mimetype: data}, metadata
991 else:
990 else:
992 return {'text/html': self._repr_html_()}
991 return {'text/html': self._repr_html_()}
993
992
994 def _data_and_metadata(self, always_both=False):
993 def _data_and_metadata(self, always_both=False):
995 """shortcut for returning metadata with shape information, if defined"""
994 """shortcut for returning metadata with shape information, if defined"""
996 try:
995 try:
997 b64_data = b2a_base64(self.data).decode('ascii')
996 b64_data = b2a_base64(self.data).decode('ascii')
998 except TypeError as e:
997 except TypeError as e:
999 raise FileNotFoundError(
998 raise FileNotFoundError(
1000 "No such file or directory: '%s'" % (self.data)) from e
999 "No such file or directory: '%s'" % (self.data)) from e
1001 md = {}
1000 md = {}
1002 if self.metadata:
1001 if self.metadata:
1003 md.update(self.metadata)
1002 md.update(self.metadata)
1004 if self.width:
1003 if self.width:
1005 md['width'] = self.width
1004 md['width'] = self.width
1006 if self.height:
1005 if self.height:
1007 md['height'] = self.height
1006 md['height'] = self.height
1008 if self.unconfined:
1007 if self.unconfined:
1009 md['unconfined'] = self.unconfined
1008 md['unconfined'] = self.unconfined
1010 if md or always_both:
1009 if md or always_both:
1011 return b64_data, md
1010 return b64_data, md
1012 else:
1011 else:
1013 return b64_data
1012 return b64_data
1014
1013
1015 def _repr_png_(self):
1014 def _repr_png_(self):
1016 if self.embed and self.format == self._FMT_PNG:
1015 if self.embed and self.format == self._FMT_PNG:
1017 return self._data_and_metadata()
1016 return self._data_and_metadata()
1018
1017
1019 def _repr_jpeg_(self):
1018 def _repr_jpeg_(self):
1020 if self.embed and self.format == self._FMT_JPEG:
1019 if self.embed and self.format == self._FMT_JPEG:
1021 return self._data_and_metadata()
1020 return self._data_and_metadata()
1022
1021
1023 def _find_ext(self, s):
1022 def _find_ext(self, s):
1024 base, ext = splitext(s)
1023 base, ext = splitext(s)
1025
1024
1026 if not ext:
1025 if not ext:
1027 return base
1026 return base
1028
1027
1029 # `splitext` includes leading period, so we skip it
1028 # `splitext` includes leading period, so we skip it
1030 return ext[1:].lower()
1029 return ext[1:].lower()
1031
1030
1032
1031
1033 class Video(DisplayObject):
1032 class Video(DisplayObject):
1034
1033
1035 def __init__(self, data=None, url=None, filename=None, embed=False,
1034 def __init__(self, data=None, url=None, filename=None, embed=False,
1036 mimetype=None, width=None, height=None, html_attributes="controls"):
1035 mimetype=None, width=None, height=None, html_attributes="controls"):
1037 """Create a video object given raw data or an URL.
1036 """Create a video object given raw data or an URL.
1038
1037
1039 When this object is returned by an input cell or passed to the
1038 When this object is returned by an input cell or passed to the
1040 display function, it will result in the video being displayed
1039 display function, it will result in the video being displayed
1041 in the frontend.
1040 in the frontend.
1042
1041
1043 Parameters
1042 Parameters
1044 ----------
1043 ----------
1045 data : unicode, str or bytes
1044 data : unicode, str or bytes
1046 The raw video data or a URL or filename to load the data from.
1045 The raw video data or a URL or filename to load the data from.
1047 Raw data will require passing `embed=True`.
1046 Raw data will require passing `embed=True`.
1048 url : unicode
1047 url : unicode
1049 A URL for the video. If you specify `url=`,
1048 A URL for the video. If you specify `url=`,
1050 the image data will not be embedded.
1049 the image data will not be embedded.
1051 filename : unicode
1050 filename : unicode
1052 Path to a local file containing the video.
1051 Path to a local file containing the video.
1053 Will be interpreted as a local URL unless `embed=True`.
1052 Will be interpreted as a local URL unless `embed=True`.
1054 embed : bool
1053 embed : bool
1055 Should the video be embedded using a data URI (True) or be
1054 Should the video be embedded using a data URI (True) or be
1056 loaded using a <video> tag (False).
1055 loaded using a <video> tag (False).
1057
1056
1058 Since videos are large, embedding them should be avoided, if possible.
1057 Since videos are large, embedding them should be avoided, if possible.
1059 You must confirm embedding as your intention by passing `embed=True`.
1058 You must confirm embedding as your intention by passing `embed=True`.
1060
1059
1061 Local files can be displayed with URLs without embedding the content, via::
1060 Local files can be displayed with URLs without embedding the content, via::
1062
1061
1063 Video('./video.mp4')
1062 Video('./video.mp4')
1064
1063 mimetype : unicode
1065 mimetype: unicode
1066 Specify the mimetype for embedded videos.
1064 Specify the mimetype for embedded videos.
1067 Default will be guessed from file extension, if available.
1065 Default will be guessed from file extension, if available.
1068 width : int
1066 width : int
1069 Width in pixels to which to constrain the video in HTML.
1067 Width in pixels to which to constrain the video in HTML.
1070 If not supplied, defaults to the width of the video.
1068 If not supplied, defaults to the width of the video.
1071 height : int
1069 height : int
1072 Height in pixels to which to constrain the video in html.
1070 Height in pixels to which to constrain the video in html.
1073 If not supplied, defaults to the height of the video.
1071 If not supplied, defaults to the height of the video.
1074 html_attributes : str
1072 html_attributes : str
1075 Attributes for the HTML `<video>` block.
1073 Attributes for the HTML `<video>` block.
1076 Default: `"controls"` to get video controls.
1074 Default: `"controls"` to get video controls.
1077 Other examples: `"controls muted"` for muted video with controls,
1075 Other examples: `"controls muted"` for muted video with controls,
1078 `"loop autoplay"` for looping autoplaying video without controls.
1076 `"loop autoplay"` for looping autoplaying video without controls.
1079
1077
1080 Examples
1078 Examples
1081 --------
1079 --------
1082
1083 ::
1080 ::
1084
1081
1085 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1082 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1086 Video('path/to/video.mp4')
1083 Video('path/to/video.mp4')
1087 Video('path/to/video.mp4', embed=True)
1084 Video('path/to/video.mp4', embed=True)
1088 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1085 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1089 Video(b'raw-videodata', embed=True)
1086 Video(b'raw-videodata', embed=True)
1090 """
1087 """
1091 if isinstance(data, (Path, PurePath)):
1088 if isinstance(data, (Path, PurePath)):
1092 data = str(data)
1089 data = str(data)
1093
1090
1094 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1091 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1095 url = data
1092 url = data
1096 data = None
1093 data = None
1097 elif data is not None and os.path.exists(data):
1094 elif data is not None and os.path.exists(data):
1098 filename = data
1095 filename = data
1099 data = None
1096 data = None
1100
1097
1101 if data and not embed:
1098 if data and not embed:
1102 msg = ''.join([
1099 msg = ''.join([
1103 "To embed videos, you must pass embed=True ",
1100 "To embed videos, you must pass embed=True ",
1104 "(this may make your notebook files huge)\n",
1101 "(this may make your notebook files huge)\n",
1105 "Consider passing Video(url='...')",
1102 "Consider passing Video(url='...')",
1106 ])
1103 ])
1107 raise ValueError(msg)
1104 raise ValueError(msg)
1108
1105
1109 self.mimetype = mimetype
1106 self.mimetype = mimetype
1110 self.embed = embed
1107 self.embed = embed
1111 self.width = width
1108 self.width = width
1112 self.height = height
1109 self.height = height
1113 self.html_attributes = html_attributes
1110 self.html_attributes = html_attributes
1114 super(Video, self).__init__(data=data, url=url, filename=filename)
1111 super(Video, self).__init__(data=data, url=url, filename=filename)
1115
1112
1116 def _repr_html_(self):
1113 def _repr_html_(self):
1117 width = height = ''
1114 width = height = ''
1118 if self.width:
1115 if self.width:
1119 width = ' width="%d"' % self.width
1116 width = ' width="%d"' % self.width
1120 if self.height:
1117 if self.height:
1121 height = ' height="%d"' % self.height
1118 height = ' height="%d"' % self.height
1122
1119
1123 # External URLs and potentially local files are not embedded into the
1120 # External URLs and potentially local files are not embedded into the
1124 # notebook output.
1121 # notebook output.
1125 if not self.embed:
1122 if not self.embed:
1126 url = self.url if self.url is not None else self.filename
1123 url = self.url if self.url is not None else self.filename
1127 output = """<video src="{0}" {1} {2} {3}>
1124 output = """<video src="{0}" {1} {2} {3}>
1128 Your browser does not support the <code>video</code> element.
1125 Your browser does not support the <code>video</code> element.
1129 </video>""".format(url, self.html_attributes, width, height)
1126 </video>""".format(url, self.html_attributes, width, height)
1130 return output
1127 return output
1131
1128
1132 # Embedded videos are base64-encoded.
1129 # Embedded videos are base64-encoded.
1133 mimetype = self.mimetype
1130 mimetype = self.mimetype
1134 if self.filename is not None:
1131 if self.filename is not None:
1135 if not mimetype:
1132 if not mimetype:
1136 mimetype, _ = mimetypes.guess_type(self.filename)
1133 mimetype, _ = mimetypes.guess_type(self.filename)
1137
1134
1138 with open(self.filename, 'rb') as f:
1135 with open(self.filename, 'rb') as f:
1139 video = f.read()
1136 video = f.read()
1140 else:
1137 else:
1141 video = self.data
1138 video = self.data
1142 if isinstance(video, str):
1139 if isinstance(video, str):
1143 # unicode input is already b64-encoded
1140 # unicode input is already b64-encoded
1144 b64_video = video
1141 b64_video = video
1145 else:
1142 else:
1146 b64_video = b2a_base64(video).decode('ascii').rstrip()
1143 b64_video = b2a_base64(video).decode('ascii').rstrip()
1147
1144
1148 output = """<video {0} {1} {2}>
1145 output = """<video {0} {1} {2}>
1149 <source src="data:{3};base64,{4}" type="{3}">
1146 <source src="data:{3};base64,{4}" type="{3}">
1150 Your browser does not support the video tag.
1147 Your browser does not support the video tag.
1151 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1148 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1152 return output
1149 return output
1153
1150
1154 def reload(self):
1151 def reload(self):
1155 # TODO
1152 # TODO
1156 pass
1153 pass
1157
1154
1158
1155
1159 @skip_doctest
1156 @skip_doctest
1160 def set_matplotlib_formats(*formats, **kwargs):
1157 def set_matplotlib_formats(*formats, **kwargs):
1161 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1158 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1162
1159
1163 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1160 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1164
1161
1165 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1162 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1166
1163
1167 To set this in your config files use the following::
1164 To set this in your config files use the following::
1168
1165
1169 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1166 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1170 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1167 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1171
1168
1172 Parameters
1169 Parameters
1173 ----------
1170 ----------
1174 *formats : strs
1171 *formats : strs
1175 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1172 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1176 **kwargs :
1173 **kwargs
1177 Keyword args will be relayed to ``figure.canvas.print_figure``.
1174 Keyword args will be relayed to ``figure.canvas.print_figure``.
1178 """
1175 """
1179 from IPython.core.interactiveshell import InteractiveShell
1176 from IPython.core.interactiveshell import InteractiveShell
1180 from IPython.core.pylabtools import select_figure_formats
1177 from IPython.core.pylabtools import select_figure_formats
1181 # build kwargs, starting with InlineBackend config
1178 # build kwargs, starting with InlineBackend config
1182 kw = {}
1179 kw = {}
1183 from ipykernel.pylab.config import InlineBackend
1180 from ipykernel.pylab.config import InlineBackend
1184 cfg = InlineBackend.instance()
1181 cfg = InlineBackend.instance()
1185 kw.update(cfg.print_figure_kwargs)
1182 kw.update(cfg.print_figure_kwargs)
1186 kw.update(**kwargs)
1183 kw.update(**kwargs)
1187 shell = InteractiveShell.instance()
1184 shell = InteractiveShell.instance()
1188 select_figure_formats(shell, formats, **kw)
1185 select_figure_formats(shell, formats, **kw)
1189
1186
1190 @skip_doctest
1187 @skip_doctest
1191 def set_matplotlib_close(close=True):
1188 def set_matplotlib_close(close=True):
1192 """Set whether the inline backend closes all figures automatically or not.
1189 """Set whether the inline backend closes all figures automatically or not.
1193
1190
1194 By default, the inline backend used in the IPython Notebook will close all
1191 By default, the inline backend used in the IPython Notebook will close all
1195 matplotlib figures automatically after each cell is run. This means that
1192 matplotlib figures automatically after each cell is run. This means that
1196 plots in different cells won't interfere. Sometimes, you may want to make
1193 plots in different cells won't interfere. Sometimes, you may want to make
1197 a plot in one cell and then refine it in later cells. This can be accomplished
1194 a plot in one cell and then refine it in later cells. This can be accomplished
1198 by::
1195 by::
1199
1196
1200 In [1]: set_matplotlib_close(False)
1197 In [1]: set_matplotlib_close(False)
1201
1198
1202 To set this in your config files use the following::
1199 To set this in your config files use the following::
1203
1200
1204 c.InlineBackend.close_figures = False
1201 c.InlineBackend.close_figures = False
1205
1202
1206 Parameters
1203 Parameters
1207 ----------
1204 ----------
1208 close : bool
1205 close : bool
1209 Should all matplotlib figures be automatically closed after each cell is
1206 Should all matplotlib figures be automatically closed after each cell is
1210 run?
1207 run?
1211 """
1208 """
1212 from ipykernel.pylab.config import InlineBackend
1209 from ipykernel.pylab.config import InlineBackend
1213 cfg = InlineBackend.instance()
1210 cfg = InlineBackend.instance()
1214 cfg.close_figures = close
1211 cfg.close_figures = close
General Comments 0
You need to be logged in to leave comments. Login now