##// END OF EJS Templates
require explicit embed=True before embedding b64-encoded video
Min RK -
Show More
@@ -1,994 +1,1004 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 from __future__ import print_function
7 from __future__ import print_function
8
8
9 try:
9 try:
10 from base64 import encodebytes as base64_encode
10 from base64 import encodebytes as base64_encode
11 except ImportError:
11 except ImportError:
12 from base64 import encodestring as base64_encode
12 from base64 import encodestring as base64_encode
13
13
14 import json
14 import json
15 import mimetypes
15 import mimetypes
16 import os
16 import os
17 import struct
17 import struct
18 import warnings
18 import warnings
19
19
20 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
20 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
21 unicode_type)
21 unicode_type)
22 from IPython.testing.skipdoctest import skip_doctest
22 from IPython.testing.skipdoctest import skip_doctest
23
23
24 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
24 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
28 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
28 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
29 'publish_display_data']
29 'publish_display_data']
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # utility functions
32 # utility functions
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 def _safe_exists(path):
35 def _safe_exists(path):
36 """Check path, but don't let exceptions raise"""
36 """Check path, but don't let exceptions raise"""
37 try:
37 try:
38 return os.path.exists(path)
38 return os.path.exists(path)
39 except Exception:
39 except Exception:
40 return False
40 return False
41
41
42 def _merge(d1, d2):
42 def _merge(d1, d2):
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
44
44
45 Updates d1 in-place
45 Updates d1 in-place
46 """
46 """
47
47
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
49 return d2
49 return d2
50 for key, value in d2.items():
50 for key, value in d2.items():
51 d1[key] = _merge(d1.get(key), value)
51 d1[key] = _merge(d1.get(key), value)
52 return d1
52 return d1
53
53
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
55 """internal implementation of all display_foo methods
55 """internal implementation of all display_foo methods
56
56
57 Parameters
57 Parameters
58 ----------
58 ----------
59 mimetype : str
59 mimetype : str
60 The mimetype to be published (e.g. 'image/png')
60 The mimetype to be published (e.g. 'image/png')
61 objs : tuple of objects
61 objs : tuple of objects
62 The Python objects to display, or if raw=True raw text data to
62 The Python objects to display, or if raw=True raw text data to
63 display.
63 display.
64 raw : bool
64 raw : bool
65 Are the data objects raw data or Python objects that need to be
65 Are the data objects raw data or Python objects that need to be
66 formatted before display? [default: False]
66 formatted before display? [default: False]
67 metadata : dict (optional)
67 metadata : dict (optional)
68 Metadata to be associated with the specific mimetype output.
68 Metadata to be associated with the specific mimetype output.
69 """
69 """
70 if metadata:
70 if metadata:
71 metadata = {mimetype: metadata}
71 metadata = {mimetype: metadata}
72 if raw:
72 if raw:
73 # turn list of pngdata into list of { 'image/png': pngdata }
73 # turn list of pngdata into list of { 'image/png': pngdata }
74 objs = [ {mimetype: obj} for obj in objs ]
74 objs = [ {mimetype: obj} for obj in objs ]
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Main functions
78 # Main functions
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81 def publish_display_data(data, metadata=None, source=None):
81 def publish_display_data(data, metadata=None, source=None):
82 """Publish data and metadata to all frontends.
82 """Publish data and metadata to all frontends.
83
83
84 See the ``display_data`` message in the messaging documentation for
84 See the ``display_data`` message in the messaging documentation for
85 more details about this message type.
85 more details about this message type.
86
86
87 The following MIME types are currently implemented:
87 The following MIME types are currently implemented:
88
88
89 * text/plain
89 * text/plain
90 * text/html
90 * text/html
91 * text/markdown
91 * text/markdown
92 * text/latex
92 * text/latex
93 * application/json
93 * application/json
94 * application/javascript
94 * application/javascript
95 * image/png
95 * image/png
96 * image/jpeg
96 * image/jpeg
97 * image/svg+xml
97 * image/svg+xml
98
98
99 Parameters
99 Parameters
100 ----------
100 ----------
101 data : dict
101 data : dict
102 A dictionary having keys that are valid MIME types (like
102 A dictionary having keys that are valid MIME types (like
103 'text/plain' or 'image/svg+xml') and values that are the data for
103 'text/plain' or 'image/svg+xml') and values that are the data for
104 that MIME type. The data itself must be a JSON'able data
104 that MIME type. The data itself must be a JSON'able data
105 structure. Minimally all data should have the 'text/plain' data,
105 structure. Minimally all data should have the 'text/plain' data,
106 which can be displayed by all frontends. If more than the plain
106 which can be displayed by all frontends. If more than the plain
107 text is given, it is up to the frontend to decide which
107 text is given, it is up to the frontend to decide which
108 representation to use.
108 representation to use.
109 metadata : dict
109 metadata : dict
110 A dictionary for metadata related to the data. This can contain
110 A dictionary for metadata related to the data. This can contain
111 arbitrary key, value pairs that frontends can use to interpret
111 arbitrary key, value pairs that frontends can use to interpret
112 the data. mime-type keys matching those in data can be used
112 the data. mime-type keys matching those in data can be used
113 to specify metadata about particular representations.
113 to specify metadata about particular representations.
114 source : str, deprecated
114 source : str, deprecated
115 Unused.
115 Unused.
116 """
116 """
117 from IPython.core.interactiveshell import InteractiveShell
117 from IPython.core.interactiveshell import InteractiveShell
118 InteractiveShell.instance().display_pub.publish(
118 InteractiveShell.instance().display_pub.publish(
119 data=data,
119 data=data,
120 metadata=metadata,
120 metadata=metadata,
121 )
121 )
122
122
123 def display(*objs, **kwargs):
123 def display(*objs, **kwargs):
124 """Display a Python object in all frontends.
124 """Display a Python object in all frontends.
125
125
126 By default all representations will be computed and sent to the frontends.
126 By default all representations will be computed and sent to the frontends.
127 Frontends can decide which representation is used and how.
127 Frontends can decide which representation is used and how.
128
128
129 Parameters
129 Parameters
130 ----------
130 ----------
131 objs : tuple of objects
131 objs : tuple of objects
132 The Python objects to display.
132 The Python objects to display.
133 raw : bool, optional
133 raw : bool, optional
134 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
134 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
135 or Python objects that need to be formatted before display? [default: False]
135 or Python objects that need to be formatted before display? [default: False]
136 include : list or tuple, optional
136 include : list or tuple, optional
137 A list of format type strings (MIME types) to include in the
137 A list of format type strings (MIME types) to include in the
138 format data dict. If this is set *only* the format types included
138 format data dict. If this is set *only* the format types included
139 in this list will be computed.
139 in this list will be computed.
140 exclude : list or tuple, optional
140 exclude : list or tuple, optional
141 A list of format type strings (MIME types) to exclude in the format
141 A list of format type strings (MIME types) to exclude in the format
142 data dict. If this is set all format types will be computed,
142 data dict. If this is set all format types will be computed,
143 except for those included in this argument.
143 except for those included in this argument.
144 metadata : dict, optional
144 metadata : dict, optional
145 A dictionary of metadata to associate with the output.
145 A dictionary of metadata to associate with the output.
146 mime-type keys in this dictionary will be associated with the individual
146 mime-type keys in this dictionary will be associated with the individual
147 representation formats, if they exist.
147 representation formats, if they exist.
148 """
148 """
149 raw = kwargs.get('raw', False)
149 raw = kwargs.get('raw', False)
150 include = kwargs.get('include')
150 include = kwargs.get('include')
151 exclude = kwargs.get('exclude')
151 exclude = kwargs.get('exclude')
152 metadata = kwargs.get('metadata')
152 metadata = kwargs.get('metadata')
153
153
154 from IPython.core.interactiveshell import InteractiveShell
154 from IPython.core.interactiveshell import InteractiveShell
155
155
156 if not raw:
156 if not raw:
157 format = InteractiveShell.instance().display_formatter.format
157 format = InteractiveShell.instance().display_formatter.format
158
158
159 for obj in objs:
159 for obj in objs:
160 if raw:
160 if raw:
161 publish_display_data(data=obj, metadata=metadata)
161 publish_display_data(data=obj, metadata=metadata)
162 else:
162 else:
163 format_dict, md_dict = format(obj, include=include, exclude=exclude)
163 format_dict, md_dict = format(obj, include=include, exclude=exclude)
164 if not format_dict:
164 if not format_dict:
165 # nothing to display (e.g. _ipython_display_ took over)
165 # nothing to display (e.g. _ipython_display_ took over)
166 continue
166 continue
167 if metadata:
167 if metadata:
168 # kwarg-specified metadata gets precedence
168 # kwarg-specified metadata gets precedence
169 _merge(md_dict, metadata)
169 _merge(md_dict, metadata)
170 publish_display_data(data=format_dict, metadata=md_dict)
170 publish_display_data(data=format_dict, metadata=md_dict)
171
171
172
172
173 def display_pretty(*objs, **kwargs):
173 def display_pretty(*objs, **kwargs):
174 """Display the pretty (default) representation of an object.
174 """Display the pretty (default) representation of an object.
175
175
176 Parameters
176 Parameters
177 ----------
177 ----------
178 objs : tuple of objects
178 objs : tuple of objects
179 The Python objects to display, or if raw=True raw text data to
179 The Python objects to display, or if raw=True raw text data to
180 display.
180 display.
181 raw : bool
181 raw : bool
182 Are the data objects raw data or Python objects that need to be
182 Are the data objects raw data or Python objects that need to be
183 formatted before display? [default: False]
183 formatted before display? [default: False]
184 metadata : dict (optional)
184 metadata : dict (optional)
185 Metadata to be associated with the specific mimetype output.
185 Metadata to be associated with the specific mimetype output.
186 """
186 """
187 _display_mimetype('text/plain', objs, **kwargs)
187 _display_mimetype('text/plain', objs, **kwargs)
188
188
189
189
190 def display_html(*objs, **kwargs):
190 def display_html(*objs, **kwargs):
191 """Display the HTML representation of an object.
191 """Display the HTML representation of an object.
192
192
193 Note: If raw=False and the object does not have a HTML
193 Note: If raw=False and the object does not have a HTML
194 representation, no HTML will be shown.
194 representation, no HTML will be shown.
195
195
196 Parameters
196 Parameters
197 ----------
197 ----------
198 objs : tuple of objects
198 objs : tuple of objects
199 The Python objects to display, or if raw=True raw HTML data to
199 The Python objects to display, or if raw=True raw HTML data to
200 display.
200 display.
201 raw : bool
201 raw : bool
202 Are the data objects raw data or Python objects that need to be
202 Are the data objects raw data or Python objects that need to be
203 formatted before display? [default: False]
203 formatted before display? [default: False]
204 metadata : dict (optional)
204 metadata : dict (optional)
205 Metadata to be associated with the specific mimetype output.
205 Metadata to be associated with the specific mimetype output.
206 """
206 """
207 _display_mimetype('text/html', objs, **kwargs)
207 _display_mimetype('text/html', objs, **kwargs)
208
208
209
209
210 def display_markdown(*objs, **kwargs):
210 def display_markdown(*objs, **kwargs):
211 """Displays the Markdown representation of an object.
211 """Displays the Markdown representation of an object.
212
212
213 Parameters
213 Parameters
214 ----------
214 ----------
215 objs : tuple of objects
215 objs : tuple of objects
216 The Python objects to display, or if raw=True raw markdown data to
216 The Python objects to display, or if raw=True raw markdown data to
217 display.
217 display.
218 raw : bool
218 raw : bool
219 Are the data objects raw data or Python objects that need to be
219 Are the data objects raw data or Python objects that need to be
220 formatted before display? [default: False]
220 formatted before display? [default: False]
221 metadata : dict (optional)
221 metadata : dict (optional)
222 Metadata to be associated with the specific mimetype output.
222 Metadata to be associated with the specific mimetype output.
223 """
223 """
224
224
225 _display_mimetype('text/markdown', objs, **kwargs)
225 _display_mimetype('text/markdown', objs, **kwargs)
226
226
227
227
228 def display_svg(*objs, **kwargs):
228 def display_svg(*objs, **kwargs):
229 """Display the SVG representation of an object.
229 """Display the SVG representation of an object.
230
230
231 Parameters
231 Parameters
232 ----------
232 ----------
233 objs : tuple of objects
233 objs : tuple of objects
234 The Python objects to display, or if raw=True raw svg data to
234 The Python objects to display, or if raw=True raw svg data to
235 display.
235 display.
236 raw : bool
236 raw : bool
237 Are the data objects raw data or Python objects that need to be
237 Are the data objects raw data or Python objects that need to be
238 formatted before display? [default: False]
238 formatted before display? [default: False]
239 metadata : dict (optional)
239 metadata : dict (optional)
240 Metadata to be associated with the specific mimetype output.
240 Metadata to be associated with the specific mimetype output.
241 """
241 """
242 _display_mimetype('image/svg+xml', objs, **kwargs)
242 _display_mimetype('image/svg+xml', objs, **kwargs)
243
243
244
244
245 def display_png(*objs, **kwargs):
245 def display_png(*objs, **kwargs):
246 """Display the PNG representation of an object.
246 """Display the PNG representation of an object.
247
247
248 Parameters
248 Parameters
249 ----------
249 ----------
250 objs : tuple of objects
250 objs : tuple of objects
251 The Python objects to display, or if raw=True raw png data to
251 The Python objects to display, or if raw=True raw png data to
252 display.
252 display.
253 raw : bool
253 raw : bool
254 Are the data objects raw data or Python objects that need to be
254 Are the data objects raw data or Python objects that need to be
255 formatted before display? [default: False]
255 formatted before display? [default: False]
256 metadata : dict (optional)
256 metadata : dict (optional)
257 Metadata to be associated with the specific mimetype output.
257 Metadata to be associated with the specific mimetype output.
258 """
258 """
259 _display_mimetype('image/png', objs, **kwargs)
259 _display_mimetype('image/png', objs, **kwargs)
260
260
261
261
262 def display_jpeg(*objs, **kwargs):
262 def display_jpeg(*objs, **kwargs):
263 """Display the JPEG representation of an object.
263 """Display the JPEG representation of an object.
264
264
265 Parameters
265 Parameters
266 ----------
266 ----------
267 objs : tuple of objects
267 objs : tuple of objects
268 The Python objects to display, or if raw=True raw JPEG data to
268 The Python objects to display, or if raw=True raw JPEG data to
269 display.
269 display.
270 raw : bool
270 raw : bool
271 Are the data objects raw data or Python objects that need to be
271 Are the data objects raw data or Python objects that need to be
272 formatted before display? [default: False]
272 formatted before display? [default: False]
273 metadata : dict (optional)
273 metadata : dict (optional)
274 Metadata to be associated with the specific mimetype output.
274 Metadata to be associated with the specific mimetype output.
275 """
275 """
276 _display_mimetype('image/jpeg', objs, **kwargs)
276 _display_mimetype('image/jpeg', objs, **kwargs)
277
277
278
278
279 def display_latex(*objs, **kwargs):
279 def display_latex(*objs, **kwargs):
280 """Display the LaTeX representation of an object.
280 """Display the LaTeX representation of an object.
281
281
282 Parameters
282 Parameters
283 ----------
283 ----------
284 objs : tuple of objects
284 objs : tuple of objects
285 The Python objects to display, or if raw=True raw latex data to
285 The Python objects to display, or if raw=True raw latex data to
286 display.
286 display.
287 raw : bool
287 raw : bool
288 Are the data objects raw data or Python objects that need to be
288 Are the data objects raw data or Python objects that need to be
289 formatted before display? [default: False]
289 formatted before display? [default: False]
290 metadata : dict (optional)
290 metadata : dict (optional)
291 Metadata to be associated with the specific mimetype output.
291 Metadata to be associated with the specific mimetype output.
292 """
292 """
293 _display_mimetype('text/latex', objs, **kwargs)
293 _display_mimetype('text/latex', objs, **kwargs)
294
294
295
295
296 def display_json(*objs, **kwargs):
296 def display_json(*objs, **kwargs):
297 """Display the JSON representation of an object.
297 """Display the JSON representation of an object.
298
298
299 Note that not many frontends support displaying JSON.
299 Note that not many frontends support displaying JSON.
300
300
301 Parameters
301 Parameters
302 ----------
302 ----------
303 objs : tuple of objects
303 objs : tuple of objects
304 The Python objects to display, or if raw=True raw json data to
304 The Python objects to display, or if raw=True raw json data to
305 display.
305 display.
306 raw : bool
306 raw : bool
307 Are the data objects raw data or Python objects that need to be
307 Are the data objects raw data or Python objects that need to be
308 formatted before display? [default: False]
308 formatted before display? [default: False]
309 metadata : dict (optional)
309 metadata : dict (optional)
310 Metadata to be associated with the specific mimetype output.
310 Metadata to be associated with the specific mimetype output.
311 """
311 """
312 _display_mimetype('application/json', objs, **kwargs)
312 _display_mimetype('application/json', objs, **kwargs)
313
313
314
314
315 def display_javascript(*objs, **kwargs):
315 def display_javascript(*objs, **kwargs):
316 """Display the Javascript representation of an object.
316 """Display the Javascript representation of an object.
317
317
318 Parameters
318 Parameters
319 ----------
319 ----------
320 objs : tuple of objects
320 objs : tuple of objects
321 The Python objects to display, or if raw=True raw javascript data to
321 The Python objects to display, or if raw=True raw javascript data to
322 display.
322 display.
323 raw : bool
323 raw : bool
324 Are the data objects raw data or Python objects that need to be
324 Are the data objects raw data or Python objects that need to be
325 formatted before display? [default: False]
325 formatted before display? [default: False]
326 metadata : dict (optional)
326 metadata : dict (optional)
327 Metadata to be associated with the specific mimetype output.
327 Metadata to be associated with the specific mimetype output.
328 """
328 """
329 _display_mimetype('application/javascript', objs, **kwargs)
329 _display_mimetype('application/javascript', objs, **kwargs)
330
330
331
331
332 def display_pdf(*objs, **kwargs):
332 def display_pdf(*objs, **kwargs):
333 """Display the PDF representation of an object.
333 """Display the PDF representation of an object.
334
334
335 Parameters
335 Parameters
336 ----------
336 ----------
337 objs : tuple of objects
337 objs : tuple of objects
338 The Python objects to display, or if raw=True raw javascript data to
338 The Python objects to display, or if raw=True raw javascript data to
339 display.
339 display.
340 raw : bool
340 raw : bool
341 Are the data objects raw data or Python objects that need to be
341 Are the data objects raw data or Python objects that need to be
342 formatted before display? [default: False]
342 formatted before display? [default: False]
343 metadata : dict (optional)
343 metadata : dict (optional)
344 Metadata to be associated with the specific mimetype output.
344 Metadata to be associated with the specific mimetype output.
345 """
345 """
346 _display_mimetype('application/pdf', objs, **kwargs)
346 _display_mimetype('application/pdf', objs, **kwargs)
347
347
348
348
349 #-----------------------------------------------------------------------------
349 #-----------------------------------------------------------------------------
350 # Smart classes
350 # Smart classes
351 #-----------------------------------------------------------------------------
351 #-----------------------------------------------------------------------------
352
352
353
353
354 class DisplayObject(object):
354 class DisplayObject(object):
355 """An object that wraps data to be displayed."""
355 """An object that wraps data to be displayed."""
356
356
357 _read_flags = 'r'
357 _read_flags = 'r'
358 _show_mem_addr = False
358 _show_mem_addr = False
359
359
360 def __init__(self, data=None, url=None, filename=None):
360 def __init__(self, data=None, url=None, filename=None):
361 """Create a display object given raw data.
361 """Create a display object given raw data.
362
362
363 When this object is returned by an expression or passed to the
363 When this object is returned by an expression or passed to the
364 display function, it will result in the data being displayed
364 display function, it will result in the data being displayed
365 in the frontend. The MIME type of the data should match the
365 in the frontend. The MIME type of the data should match the
366 subclasses used, so the Png subclass should be used for 'image/png'
366 subclasses used, so the Png subclass should be used for 'image/png'
367 data. If the data is a URL, the data will first be downloaded
367 data. If the data is a URL, the data will first be downloaded
368 and then displayed. If
368 and then displayed. If
369
369
370 Parameters
370 Parameters
371 ----------
371 ----------
372 data : unicode, str or bytes
372 data : unicode, str or bytes
373 The raw data or a URL or file to load the data from
373 The raw data or a URL or file to load the data from
374 url : unicode
374 url : unicode
375 A URL to download the data from.
375 A URL to download the data from.
376 filename : unicode
376 filename : unicode
377 Path to a local file to load the data from.
377 Path to a local file to load the data from.
378 """
378 """
379 if data is not None and isinstance(data, string_types):
379 if data is not None and isinstance(data, string_types):
380 if data.startswith('http') and url is None:
380 if data.startswith('http') and url is None:
381 url = data
381 url = data
382 filename = None
382 filename = None
383 data = None
383 data = None
384 elif _safe_exists(data) and filename is None:
384 elif _safe_exists(data) and filename is None:
385 url = None
385 url = None
386 filename = data
386 filename = data
387 data = None
387 data = None
388
388
389 self.data = data
389 self.data = data
390 self.url = url
390 self.url = url
391 self.filename = None if filename is None else unicode_type(filename)
391 self.filename = None if filename is None else unicode_type(filename)
392
392
393 self.reload()
393 self.reload()
394 self._check_data()
394 self._check_data()
395
395
396 def __repr__(self):
396 def __repr__(self):
397 if not self._show_mem_addr:
397 if not self._show_mem_addr:
398 cls = self.__class__
398 cls = self.__class__
399 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
399 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
400 else:
400 else:
401 r = super(DisplayObject, self).__repr__()
401 r = super(DisplayObject, self).__repr__()
402 return r
402 return r
403
403
404 def _check_data(self):
404 def _check_data(self):
405 """Override in subclasses if there's something to check."""
405 """Override in subclasses if there's something to check."""
406 pass
406 pass
407
407
408 def reload(self):
408 def reload(self):
409 """Reload the raw data from file or URL."""
409 """Reload the raw data from file or URL."""
410 if self.filename is not None:
410 if self.filename is not None:
411 with open(self.filename, self._read_flags) as f:
411 with open(self.filename, self._read_flags) as f:
412 self.data = f.read()
412 self.data = f.read()
413 elif self.url is not None:
413 elif self.url is not None:
414 try:
414 try:
415 try:
415 try:
416 from urllib.request import urlopen # Py3
416 from urllib.request import urlopen # Py3
417 except ImportError:
417 except ImportError:
418 from urllib2 import urlopen
418 from urllib2 import urlopen
419 response = urlopen(self.url)
419 response = urlopen(self.url)
420 self.data = response.read()
420 self.data = response.read()
421 # extract encoding from header, if there is one:
421 # extract encoding from header, if there is one:
422 encoding = None
422 encoding = None
423 for sub in response.headers['content-type'].split(';'):
423 for sub in response.headers['content-type'].split(';'):
424 sub = sub.strip()
424 sub = sub.strip()
425 if sub.startswith('charset'):
425 if sub.startswith('charset'):
426 encoding = sub.split('=')[-1].strip()
426 encoding = sub.split('=')[-1].strip()
427 break
427 break
428 # decode data, if an encoding was specified
428 # decode data, if an encoding was specified
429 if encoding:
429 if encoding:
430 self.data = self.data.decode(encoding, 'replace')
430 self.data = self.data.decode(encoding, 'replace')
431 except:
431 except:
432 self.data = None
432 self.data = None
433
433
434 class TextDisplayObject(DisplayObject):
434 class TextDisplayObject(DisplayObject):
435 """Validate that display data is text"""
435 """Validate that display data is text"""
436 def _check_data(self):
436 def _check_data(self):
437 if self.data is not None and not isinstance(self.data, string_types):
437 if self.data is not None and not isinstance(self.data, string_types):
438 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
438 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
439
439
440 class Pretty(TextDisplayObject):
440 class Pretty(TextDisplayObject):
441
441
442 def _repr_pretty_(self):
442 def _repr_pretty_(self):
443 return self.data
443 return self.data
444
444
445
445
446 class HTML(TextDisplayObject):
446 class HTML(TextDisplayObject):
447
447
448 def _repr_html_(self):
448 def _repr_html_(self):
449 return self.data
449 return self.data
450
450
451 def __html__(self):
451 def __html__(self):
452 """
452 """
453 This method exists to inform other HTML-using modules (e.g. Markupsafe,
453 This method exists to inform other HTML-using modules (e.g. Markupsafe,
454 htmltag, etc) that this object is HTML and does not need things like
454 htmltag, etc) that this object is HTML and does not need things like
455 special characters (<>&) escaped.
455 special characters (<>&) escaped.
456 """
456 """
457 return self._repr_html_()
457 return self._repr_html_()
458
458
459
459
460 class Markdown(TextDisplayObject):
460 class Markdown(TextDisplayObject):
461
461
462 def _repr_markdown_(self):
462 def _repr_markdown_(self):
463 return self.data
463 return self.data
464
464
465
465
466 class Math(TextDisplayObject):
466 class Math(TextDisplayObject):
467
467
468 def _repr_latex_(self):
468 def _repr_latex_(self):
469 s = self.data.strip('$')
469 s = self.data.strip('$')
470 return "$$%s$$" % s
470 return "$$%s$$" % s
471
471
472
472
473 class Latex(TextDisplayObject):
473 class Latex(TextDisplayObject):
474
474
475 def _repr_latex_(self):
475 def _repr_latex_(self):
476 return self.data
476 return self.data
477
477
478
478
479 class SVG(DisplayObject):
479 class SVG(DisplayObject):
480
480
481 # wrap data in a property, which extracts the <svg> tag, discarding
481 # wrap data in a property, which extracts the <svg> tag, discarding
482 # document headers
482 # document headers
483 _data = None
483 _data = None
484
484
485 @property
485 @property
486 def data(self):
486 def data(self):
487 return self._data
487 return self._data
488
488
489 @data.setter
489 @data.setter
490 def data(self, svg):
490 def data(self, svg):
491 if svg is None:
491 if svg is None:
492 self._data = None
492 self._data = None
493 return
493 return
494 # parse into dom object
494 # parse into dom object
495 from xml.dom import minidom
495 from xml.dom import minidom
496 svg = cast_bytes_py2(svg)
496 svg = cast_bytes_py2(svg)
497 x = minidom.parseString(svg)
497 x = minidom.parseString(svg)
498 # get svg tag (should be 1)
498 # get svg tag (should be 1)
499 found_svg = x.getElementsByTagName('svg')
499 found_svg = x.getElementsByTagName('svg')
500 if found_svg:
500 if found_svg:
501 svg = found_svg[0].toxml()
501 svg = found_svg[0].toxml()
502 else:
502 else:
503 # fallback on the input, trust the user
503 # fallback on the input, trust the user
504 # but this is probably an error.
504 # but this is probably an error.
505 pass
505 pass
506 svg = cast_unicode(svg)
506 svg = cast_unicode(svg)
507 self._data = svg
507 self._data = svg
508
508
509 def _repr_svg_(self):
509 def _repr_svg_(self):
510 return self.data
510 return self.data
511
511
512
512
513 class JSON(DisplayObject):
513 class JSON(DisplayObject):
514 """JSON expects a JSON-able dict or list
514 """JSON expects a JSON-able dict or list
515
515
516 not an already-serialized JSON string.
516 not an already-serialized JSON string.
517
517
518 Scalar types (None, number, string) are not allowed, only dict or list containers.
518 Scalar types (None, number, string) are not allowed, only dict or list containers.
519 """
519 """
520 # wrap data in a property, which warns about passing already-serialized JSON
520 # wrap data in a property, which warns about passing already-serialized JSON
521 _data = None
521 _data = None
522 def _check_data(self):
522 def _check_data(self):
523 if self.data is not None and not isinstance(self.data, (dict, list)):
523 if self.data is not None and not isinstance(self.data, (dict, list)):
524 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
524 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
525
525
526 @property
526 @property
527 def data(self):
527 def data(self):
528 return self._data
528 return self._data
529
529
530 @data.setter
530 @data.setter
531 def data(self, data):
531 def data(self, data):
532 if isinstance(data, string_types):
532 if isinstance(data, string_types):
533 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
533 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
534 data = json.loads(data)
534 data = json.loads(data)
535 self._data = data
535 self._data = data
536
536
537 def _repr_json_(self):
537 def _repr_json_(self):
538 return self.data
538 return self.data
539
539
540 css_t = """$("head").append($("<link/>").attr({
540 css_t = """$("head").append($("<link/>").attr({
541 rel: "stylesheet",
541 rel: "stylesheet",
542 type: "text/css",
542 type: "text/css",
543 href: "%s"
543 href: "%s"
544 }));
544 }));
545 """
545 """
546
546
547 lib_t1 = """$.getScript("%s", function () {
547 lib_t1 = """$.getScript("%s", function () {
548 """
548 """
549 lib_t2 = """});
549 lib_t2 = """});
550 """
550 """
551
551
552 class Javascript(TextDisplayObject):
552 class Javascript(TextDisplayObject):
553
553
554 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
554 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
555 """Create a Javascript display object given raw data.
555 """Create a Javascript display object given raw data.
556
556
557 When this object is returned by an expression or passed to the
557 When this object is returned by an expression or passed to the
558 display function, it will result in the data being displayed
558 display function, it will result in the data being displayed
559 in the frontend. If the data is a URL, the data will first be
559 in the frontend. If the data is a URL, the data will first be
560 downloaded and then displayed.
560 downloaded and then displayed.
561
561
562 In the Notebook, the containing element will be available as `element`,
562 In the Notebook, the containing element will be available as `element`,
563 and jQuery will be available. Content appended to `element` will be
563 and jQuery will be available. Content appended to `element` will be
564 visible in the output area.
564 visible in the output area.
565
565
566 Parameters
566 Parameters
567 ----------
567 ----------
568 data : unicode, str or bytes
568 data : unicode, str or bytes
569 The Javascript source code or a URL to download it from.
569 The Javascript source code or a URL to download it from.
570 url : unicode
570 url : unicode
571 A URL to download the data from.
571 A URL to download the data from.
572 filename : unicode
572 filename : unicode
573 Path to a local file to load the data from.
573 Path to a local file to load the data from.
574 lib : list or str
574 lib : list or str
575 A sequence of Javascript library URLs to load asynchronously before
575 A sequence of Javascript library URLs to load asynchronously before
576 running the source code. The full URLs of the libraries should
576 running the source code. The full URLs of the libraries should
577 be given. A single Javascript library URL can also be given as a
577 be given. A single Javascript library URL can also be given as a
578 string.
578 string.
579 css: : list or str
579 css: : list or str
580 A sequence of css files to load before running the source code.
580 A sequence of css files to load before running the source code.
581 The full URLs of the css files should be given. A single css URL
581 The full URLs of the css files should be given. A single css URL
582 can also be given as a string.
582 can also be given as a string.
583 """
583 """
584 if isinstance(lib, string_types):
584 if isinstance(lib, string_types):
585 lib = [lib]
585 lib = [lib]
586 elif lib is None:
586 elif lib is None:
587 lib = []
587 lib = []
588 if isinstance(css, string_types):
588 if isinstance(css, string_types):
589 css = [css]
589 css = [css]
590 elif css is None:
590 elif css is None:
591 css = []
591 css = []
592 if not isinstance(lib, (list,tuple)):
592 if not isinstance(lib, (list,tuple)):
593 raise TypeError('expected sequence, got: %r' % lib)
593 raise TypeError('expected sequence, got: %r' % lib)
594 if not isinstance(css, (list,tuple)):
594 if not isinstance(css, (list,tuple)):
595 raise TypeError('expected sequence, got: %r' % css)
595 raise TypeError('expected sequence, got: %r' % css)
596 self.lib = lib
596 self.lib = lib
597 self.css = css
597 self.css = css
598 super(Javascript, self).__init__(data=data, url=url, filename=filename)
598 super(Javascript, self).__init__(data=data, url=url, filename=filename)
599
599
600 def _repr_javascript_(self):
600 def _repr_javascript_(self):
601 r = ''
601 r = ''
602 for c in self.css:
602 for c in self.css:
603 r += css_t % c
603 r += css_t % c
604 for l in self.lib:
604 for l in self.lib:
605 r += lib_t1 % l
605 r += lib_t1 % l
606 r += self.data
606 r += self.data
607 r += lib_t2*len(self.lib)
607 r += lib_t2*len(self.lib)
608 return r
608 return r
609
609
610 # constants for identifying png/jpeg data
610 # constants for identifying png/jpeg data
611 _PNG = b'\x89PNG\r\n\x1a\n'
611 _PNG = b'\x89PNG\r\n\x1a\n'
612 _JPEG = b'\xff\xd8'
612 _JPEG = b'\xff\xd8'
613
613
614 def _pngxy(data):
614 def _pngxy(data):
615 """read the (width, height) from a PNG header"""
615 """read the (width, height) from a PNG header"""
616 ihdr = data.index(b'IHDR')
616 ihdr = data.index(b'IHDR')
617 # next 8 bytes are width/height
617 # next 8 bytes are width/height
618 w4h4 = data[ihdr+4:ihdr+12]
618 w4h4 = data[ihdr+4:ihdr+12]
619 return struct.unpack('>ii', w4h4)
619 return struct.unpack('>ii', w4h4)
620
620
621 def _jpegxy(data):
621 def _jpegxy(data):
622 """read the (width, height) from a JPEG header"""
622 """read the (width, height) from a JPEG header"""
623 # adapted from http://www.64lines.com/jpeg-width-height
623 # adapted from http://www.64lines.com/jpeg-width-height
624
624
625 idx = 4
625 idx = 4
626 while True:
626 while True:
627 block_size = struct.unpack('>H', data[idx:idx+2])[0]
627 block_size = struct.unpack('>H', data[idx:idx+2])[0]
628 idx = idx + block_size
628 idx = idx + block_size
629 if data[idx:idx+2] == b'\xFF\xC0':
629 if data[idx:idx+2] == b'\xFF\xC0':
630 # found Start of Frame
630 # found Start of Frame
631 iSOF = idx
631 iSOF = idx
632 break
632 break
633 else:
633 else:
634 # read another block
634 # read another block
635 idx += 2
635 idx += 2
636
636
637 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
637 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
638 return w, h
638 return w, h
639
639
640 class Image(DisplayObject):
640 class Image(DisplayObject):
641
641
642 _read_flags = 'rb'
642 _read_flags = 'rb'
643 _FMT_JPEG = u'jpeg'
643 _FMT_JPEG = u'jpeg'
644 _FMT_PNG = u'png'
644 _FMT_PNG = u'png'
645 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
645 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
646
646
647 def __init__(self, data=None, url=None, filename=None, format=None,
647 def __init__(self, data=None, url=None, filename=None, format=None,
648 embed=None, width=None, height=None, retina=False,
648 embed=None, width=None, height=None, retina=False,
649 unconfined=False, metadata=None):
649 unconfined=False, metadata=None):
650 """Create a PNG/JPEG image object given raw data.
650 """Create a PNG/JPEG image object given raw data.
651
651
652 When this object is returned by an input cell or passed to the
652 When this object is returned by an input cell or passed to the
653 display function, it will result in the image being displayed
653 display function, it will result in the image being displayed
654 in the frontend.
654 in the frontend.
655
655
656 Parameters
656 Parameters
657 ----------
657 ----------
658 data : unicode, str or bytes
658 data : unicode, str or bytes
659 The raw image data or a URL or filename to load the data from.
659 The raw image data or a URL or filename to load the data from.
660 This always results in embedded image data.
660 This always results in embedded image data.
661 url : unicode
661 url : unicode
662 A URL to download the data from. If you specify `url=`,
662 A URL to download the data from. If you specify `url=`,
663 the image data will not be embedded unless you also specify `embed=True`.
663 the image data will not be embedded unless you also specify `embed=True`.
664 filename : unicode
664 filename : unicode
665 Path to a local file to load the data from.
665 Path to a local file to load the data from.
666 Images from a file are always embedded.
666 Images from a file are always embedded.
667 format : unicode
667 format : unicode
668 The format of the image data (png/jpeg/jpg). If a filename or URL is given
668 The format of the image data (png/jpeg/jpg). If a filename or URL is given
669 for format will be inferred from the filename extension.
669 for format will be inferred from the filename extension.
670 embed : bool
670 embed : bool
671 Should the image data be embedded using a data URI (True) or be
671 Should the image data be embedded using a data URI (True) or be
672 loaded using an <img> tag. Set this to True if you want the image
672 loaded using an <img> tag. Set this to True if you want the image
673 to be viewable later with no internet connection in the notebook.
673 to be viewable later with no internet connection in the notebook.
674
674
675 Default is `True`, unless the keyword argument `url` is set, then
675 Default is `True`, unless the keyword argument `url` is set, then
676 default value is `False`.
676 default value is `False`.
677
677
678 Note that QtConsole is not able to display images if `embed` is set to `False`
678 Note that QtConsole is not able to display images if `embed` is set to `False`
679 width : int
679 width : int
680 Width to which to constrain the image in html
680 Width to which to constrain the image in html
681 height : int
681 height : int
682 Height to which to constrain the image in html
682 Height to which to constrain the image in html
683 retina : bool
683 retina : bool
684 Automatically set the width and height to half of the measured
684 Automatically set the width and height to half of the measured
685 width and height.
685 width and height.
686 This only works for embedded images because it reads the width/height
686 This only works for embedded images because it reads the width/height
687 from image data.
687 from image data.
688 For non-embedded images, you can just set the desired display width
688 For non-embedded images, you can just set the desired display width
689 and height directly.
689 and height directly.
690 unconfined: bool
690 unconfined: bool
691 Set unconfined=True to disable max-width confinement of the image.
691 Set unconfined=True to disable max-width confinement of the image.
692 metadata: dict
692 metadata: dict
693 Specify extra metadata to attach to the image.
693 Specify extra metadata to attach to the image.
694
694
695 Examples
695 Examples
696 --------
696 --------
697 # embedded image data, works in qtconsole and notebook
697 # embedded image data, works in qtconsole and notebook
698 # when passed positionally, the first arg can be any of raw image data,
698 # when passed positionally, the first arg can be any of raw image data,
699 # a URL, or a filename from which to load image data.
699 # a URL, or a filename from which to load image data.
700 # The result is always embedding image data for inline images.
700 # The result is always embedding image data for inline images.
701 Image('http://www.google.fr/images/srpr/logo3w.png')
701 Image('http://www.google.fr/images/srpr/logo3w.png')
702 Image('/path/to/image.jpg')
702 Image('/path/to/image.jpg')
703 Image(b'RAW_PNG_DATA...')
703 Image(b'RAW_PNG_DATA...')
704
704
705 # Specifying Image(url=...) does not embed the image data,
705 # Specifying Image(url=...) does not embed the image data,
706 # it only generates `<img>` tag with a link to the source.
706 # it only generates `<img>` tag with a link to the source.
707 # This will not work in the qtconsole or offline.
707 # This will not work in the qtconsole or offline.
708 Image(url='http://www.google.fr/images/srpr/logo3w.png')
708 Image(url='http://www.google.fr/images/srpr/logo3w.png')
709
709
710 """
710 """
711 if filename is not None:
711 if filename is not None:
712 ext = self._find_ext(filename)
712 ext = self._find_ext(filename)
713 elif url is not None:
713 elif url is not None:
714 ext = self._find_ext(url)
714 ext = self._find_ext(url)
715 elif data is None:
715 elif data is None:
716 raise ValueError("No image data found. Expecting filename, url, or data.")
716 raise ValueError("No image data found. Expecting filename, url, or data.")
717 elif isinstance(data, string_types) and (
717 elif isinstance(data, string_types) and (
718 data.startswith('http') or _safe_exists(data)
718 data.startswith('http') or _safe_exists(data)
719 ):
719 ):
720 ext = self._find_ext(data)
720 ext = self._find_ext(data)
721 else:
721 else:
722 ext = None
722 ext = None
723
723
724 if format is None:
724 if format is None:
725 if ext is not None:
725 if ext is not None:
726 if ext == u'jpg' or ext == u'jpeg':
726 if ext == u'jpg' or ext == u'jpeg':
727 format = self._FMT_JPEG
727 format = self._FMT_JPEG
728 if ext == u'png':
728 if ext == u'png':
729 format = self._FMT_PNG
729 format = self._FMT_PNG
730 else:
730 else:
731 format = ext.lower()
731 format = ext.lower()
732 elif isinstance(data, bytes):
732 elif isinstance(data, bytes):
733 # infer image type from image data header,
733 # infer image type from image data header,
734 # only if format has not been specified.
734 # only if format has not been specified.
735 if data[:2] == _JPEG:
735 if data[:2] == _JPEG:
736 format = self._FMT_JPEG
736 format = self._FMT_JPEG
737
737
738 # failed to detect format, default png
738 # failed to detect format, default png
739 if format is None:
739 if format is None:
740 format = 'png'
740 format = 'png'
741
741
742 if format.lower() == 'jpg':
742 if format.lower() == 'jpg':
743 # jpg->jpeg
743 # jpg->jpeg
744 format = self._FMT_JPEG
744 format = self._FMT_JPEG
745
745
746 self.format = unicode_type(format).lower()
746 self.format = unicode_type(format).lower()
747 self.embed = embed if embed is not None else (url is None)
747 self.embed = embed if embed is not None else (url is None)
748
748
749 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
749 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
750 raise ValueError("Cannot embed the '%s' image format" % (self.format))
750 raise ValueError("Cannot embed the '%s' image format" % (self.format))
751 self.width = width
751 self.width = width
752 self.height = height
752 self.height = height
753 self.retina = retina
753 self.retina = retina
754 self.unconfined = unconfined
754 self.unconfined = unconfined
755 self.metadata = metadata
755 self.metadata = metadata
756 super(Image, self).__init__(data=data, url=url, filename=filename)
756 super(Image, self).__init__(data=data, url=url, filename=filename)
757
757
758 if retina:
758 if retina:
759 self._retina_shape()
759 self._retina_shape()
760
760
761 def _retina_shape(self):
761 def _retina_shape(self):
762 """load pixel-doubled width and height from image data"""
762 """load pixel-doubled width and height from image data"""
763 if not self.embed:
763 if not self.embed:
764 return
764 return
765 if self.format == 'png':
765 if self.format == 'png':
766 w, h = _pngxy(self.data)
766 w, h = _pngxy(self.data)
767 elif self.format == 'jpeg':
767 elif self.format == 'jpeg':
768 w, h = _jpegxy(self.data)
768 w, h = _jpegxy(self.data)
769 else:
769 else:
770 # retina only supports png
770 # retina only supports png
771 return
771 return
772 self.width = w // 2
772 self.width = w // 2
773 self.height = h // 2
773 self.height = h // 2
774
774
775 def reload(self):
775 def reload(self):
776 """Reload the raw data from file or URL."""
776 """Reload the raw data from file or URL."""
777 if self.embed:
777 if self.embed:
778 super(Image,self).reload()
778 super(Image,self).reload()
779 if self.retina:
779 if self.retina:
780 self._retina_shape()
780 self._retina_shape()
781
781
782 def _repr_html_(self):
782 def _repr_html_(self):
783 if not self.embed:
783 if not self.embed:
784 width = height = klass = ''
784 width = height = klass = ''
785 if self.width:
785 if self.width:
786 width = ' width="%d"' % self.width
786 width = ' width="%d"' % self.width
787 if self.height:
787 if self.height:
788 height = ' height="%d"' % self.height
788 height = ' height="%d"' % self.height
789 if self.unconfined:
789 if self.unconfined:
790 klass = ' class="unconfined"'
790 klass = ' class="unconfined"'
791 return u'<img src="{url}"{width}{height}{klass}/>'.format(
791 return u'<img src="{url}"{width}{height}{klass}/>'.format(
792 url=self.url,
792 url=self.url,
793 width=width,
793 width=width,
794 height=height,
794 height=height,
795 klass=klass,
795 klass=klass,
796 )
796 )
797
797
798 def _data_and_metadata(self):
798 def _data_and_metadata(self):
799 """shortcut for returning metadata with shape information, if defined"""
799 """shortcut for returning metadata with shape information, if defined"""
800 md = {}
800 md = {}
801 if self.width:
801 if self.width:
802 md['width'] = self.width
802 md['width'] = self.width
803 if self.height:
803 if self.height:
804 md['height'] = self.height
804 md['height'] = self.height
805 if self.unconfined:
805 if self.unconfined:
806 md['unconfined'] = self.unconfined
806 md['unconfined'] = self.unconfined
807 if self.metadata:
807 if self.metadata:
808 md.update(self.metadata)
808 md.update(self.metadata)
809 if md:
809 if md:
810 return self.data, md
810 return self.data, md
811 else:
811 else:
812 return self.data
812 return self.data
813
813
814 def _repr_png_(self):
814 def _repr_png_(self):
815 if self.embed and self.format == u'png':
815 if self.embed and self.format == u'png':
816 return self._data_and_metadata()
816 return self._data_and_metadata()
817
817
818 def _repr_jpeg_(self):
818 def _repr_jpeg_(self):
819 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
819 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
820 return self._data_and_metadata()
820 return self._data_and_metadata()
821
821
822 def _find_ext(self, s):
822 def _find_ext(self, s):
823 return unicode_type(s.split('.')[-1].lower())
823 return unicode_type(s.split('.')[-1].lower())
824
824
825 class Video(DisplayObject):
825 class Video(DisplayObject):
826
826
827 def __init__(self, data=None, url=None, filename=None, embed=None, mimetype=None):
827 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
828 """Create a video object given raw data or an URL.
828 """Create a video object given raw data or an URL.
829
829
830 When this object is returned by an input cell or passed to the
830 When this object is returned by an input cell or passed to the
831 display function, it will result in the video being displayed
831 display function, it will result in the video being displayed
832 in the frontend.
832 in the frontend.
833
833
834 Parameters
834 Parameters
835 ----------
835 ----------
836 data : unicode, str or bytes
836 data : unicode, str or bytes
837 The raw image data or a URL or filename to load the data from.
837 The raw video data or a URL or filename to load the data from.
838 This always results in embedded image data.
838 Raw data will require passing `embed=True`.
839 url : unicode
839 url : unicode
840 A URL to download the data from. If you specify `url=`,
840 A URL for the video. If you specify `url=`,
841 the image data will not be embedded unless you also specify `embed=True`.
841 the image data will not be embedded.
842 filename : unicode
842 filename : unicode
843 Path to a local file to load the data from.
843 Path to a local file containing the video.
844 Videos from a file are always embedded.
844 Will be interpreted as a local URL unless `embed=True`.
845 embed : bool
845 embed : bool
846 Should the image data be embedded using a data URI (True) or be
846 Should the video be embedded using a data URI (True) or be
847 loaded using an <img> tag. Set this to True if you want the image
847 loaded using a <video> tag (False).
848 to be viewable later with no internet connection in the notebook.
849
848
850 Default is `True`, unless the keyword argument `url` is set, then
849 Since videos are large, embedding them should be avoided, if possible.
851 default value is `False`.
850 You must confirm embedding as your intention by passing `embed=True`.
851
852 Local files can be displayed with URLs without embedding the content, via::
853
854 Video('./video.mp4')
852
855
853 Note that QtConsole is not able to display images if `embed` is set to `False`
854 mimetype: unicode
856 mimetype: unicode
855 Specify the mimetype in case you load in a encoded video.
857 Specify the mimetype for embedded videos.
858 Default will be guessed from file extension, if available.
856
859
857 Examples
860 Examples
858 --------
861 --------
859
862
860 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
863 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
861 Video('path/to/video.mp4')
864 Video('path/to/video.mp4')
862 Video('path/to/video.mp4', embed=False)
865 Video('path/to/video.mp4', embed=True)
863 Video(b'videodata')
866 Video(b'raw-videodata', embed=True)
864 Video(u'b64-encoded-videodata')
865 """
867 """
866 if url is None and isinstance(data, string_types) and data.startswith(('http:', 'https:')):
868 if url is None and isinstance(data, string_types) and data.startswith(('http:', 'https:')):
867 url = data
869 url = data
868 data = None
870 data = None
869 embed = False
870 elif os.path.exists(data):
871 elif os.path.exists(data):
871 filename = data
872 filename = data
872 data = None
873 data = None
874
875 if data and not embed:
876 msg = ''.join([
877 "To embed videos, you must pass embed=True ",
878 "(this may make your notebook files huge)\n",
879 "Consider passing Video(url='...')",
880 ])
881 raise ValueError(msg)
873
882
874 self.mimetype = mimetype
883 self.mimetype = mimetype
875 self.embed = embed if embed is not None else (filename is not None)
884 self.embed = embed
876 super(Video, self).__init__(data=data, url=url, filename=filename)
885 super(Video, self).__init__(data=data, url=url, filename=filename)
877
886
878 def _repr_html_(self):
887 def _repr_html_(self):
879 # External URLs and potentially local files are not embedded into the
888 # External URLs and potentially local files are not embedded into the
880 # notebook output.
889 # notebook output.
881 if not self.embed:
890 if not self.embed:
882 url = self.url if self.url is not None else self.filename
891 url = self.url if self.url is not None else self.filename
883 output = """<video src="{0}" controls>
892 output = """<video src="{0}" controls>
884 Your browser does not support the <code>video</code> element.
893 Your browser does not support the <code>video</code> element.
885 </video>""".format(url)
894 </video>""".format(url)
886 return output
895 return output
887 # Embedded videos uses base64 encoded videos.
896
897 # Embedded videos are base64-encoded.
888 mimetype = self.mimetype
898 mimetype = self.mimetype
889 if self.filename is not None:
899 if self.filename is not None:
890 if not mimetype:
900 if not mimetype:
891 mimetype, _ = mimetypes.guess_type(self.filename)
901 mimetype, _ = mimetypes.guess_type(self.filename)
892
902
893 with open(self.filename, 'rb') as f:
903 with open(self.filename, 'rb') as f:
894 video = f.read()
904 video = f.read()
895 else:
905 else:
896 video = self.data
906 video = self.data
897 if isinstance(video, unicode_type):
907 if isinstance(video, unicode_type):
898 # unicode input is already b64-encoded
908 # unicode input is already b64-encoded
899 b64_video = video
909 b64_video = video
900 else:
910 else:
901 b64_video = base64_encode(video).decode('ascii').rstrip()
911 b64_video = base64_encode(video).decode('ascii').rstrip()
902
912
903 output = """<video controls>
913 output = """<video controls>
904 <source src="data:{0};base64,{1}" type="{0}">
914 <source src="data:{0};base64,{1}" type="{0}">
905 Your browser does not support the video tag.
915 Your browser does not support the video tag.
906 </video>""".format(mimetype, b64_video)
916 </video>""".format(mimetype, b64_video)
907 return output
917 return output
908
918
909 def reload(self):
919 def reload(self):
910 # TODO
920 # TODO
911 pass
921 pass
912
922
913 def _repr_png_(self):
923 def _repr_png_(self):
914 # TODO
924 # TODO
915 pass
925 pass
916 def _repr_jpeg_(self):
926 def _repr_jpeg_(self):
917 # TODO
927 # TODO
918 pass
928 pass
919
929
920 def clear_output(wait=False):
930 def clear_output(wait=False):
921 """Clear the output of the current cell receiving output.
931 """Clear the output of the current cell receiving output.
922
932
923 Parameters
933 Parameters
924 ----------
934 ----------
925 wait : bool [default: false]
935 wait : bool [default: false]
926 Wait to clear the output until new output is available to replace it."""
936 Wait to clear the output until new output is available to replace it."""
927 from IPython.core.interactiveshell import InteractiveShell
937 from IPython.core.interactiveshell import InteractiveShell
928 if InteractiveShell.initialized():
938 if InteractiveShell.initialized():
929 InteractiveShell.instance().display_pub.clear_output(wait)
939 InteractiveShell.instance().display_pub.clear_output(wait)
930 else:
940 else:
931 from IPython.utils import io
941 from IPython.utils import io
932 print('\033[2K\r', file=io.stdout, end='')
942 print('\033[2K\r', file=io.stdout, end='')
933 io.stdout.flush()
943 io.stdout.flush()
934 print('\033[2K\r', file=io.stderr, end='')
944 print('\033[2K\r', file=io.stderr, end='')
935 io.stderr.flush()
945 io.stderr.flush()
936
946
937
947
938 @skip_doctest
948 @skip_doctest
939 def set_matplotlib_formats(*formats, **kwargs):
949 def set_matplotlib_formats(*formats, **kwargs):
940 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
950 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
941
951
942 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
952 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
943
953
944 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
954 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
945
955
946 To set this in your config files use the following::
956 To set this in your config files use the following::
947
957
948 c.InlineBackend.figure_formats = {'png', 'jpeg'}
958 c.InlineBackend.figure_formats = {'png', 'jpeg'}
949 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
959 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
950
960
951 Parameters
961 Parameters
952 ----------
962 ----------
953 *formats : strs
963 *formats : strs
954 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
964 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
955 **kwargs :
965 **kwargs :
956 Keyword args will be relayed to ``figure.canvas.print_figure``.
966 Keyword args will be relayed to ``figure.canvas.print_figure``.
957 """
967 """
958 from IPython.core.interactiveshell import InteractiveShell
968 from IPython.core.interactiveshell import InteractiveShell
959 from IPython.core.pylabtools import select_figure_formats
969 from IPython.core.pylabtools import select_figure_formats
960 # build kwargs, starting with InlineBackend config
970 # build kwargs, starting with InlineBackend config
961 kw = {}
971 kw = {}
962 from ipykernel.pylab.config import InlineBackend
972 from ipykernel.pylab.config import InlineBackend
963 cfg = InlineBackend.instance()
973 cfg = InlineBackend.instance()
964 kw.update(cfg.print_figure_kwargs)
974 kw.update(cfg.print_figure_kwargs)
965 kw.update(**kwargs)
975 kw.update(**kwargs)
966 shell = InteractiveShell.instance()
976 shell = InteractiveShell.instance()
967 select_figure_formats(shell, formats, **kw)
977 select_figure_formats(shell, formats, **kw)
968
978
969 @skip_doctest
979 @skip_doctest
970 def set_matplotlib_close(close=True):
980 def set_matplotlib_close(close=True):
971 """Set whether the inline backend closes all figures automatically or not.
981 """Set whether the inline backend closes all figures automatically or not.
972
982
973 By default, the inline backend used in the IPython Notebook will close all
983 By default, the inline backend used in the IPython Notebook will close all
974 matplotlib figures automatically after each cell is run. This means that
984 matplotlib figures automatically after each cell is run. This means that
975 plots in different cells won't interfere. Sometimes, you may want to make
985 plots in different cells won't interfere. Sometimes, you may want to make
976 a plot in one cell and then refine it in later cells. This can be accomplished
986 a plot in one cell and then refine it in later cells. This can be accomplished
977 by::
987 by::
978
988
979 In [1]: set_matplotlib_close(False)
989 In [1]: set_matplotlib_close(False)
980
990
981 To set this in your config files use the following::
991 To set this in your config files use the following::
982
992
983 c.InlineBackend.close_figures = False
993 c.InlineBackend.close_figures = False
984
994
985 Parameters
995 Parameters
986 ----------
996 ----------
987 close : bool
997 close : bool
988 Should all matplotlib figures be automatically closed after each cell is
998 Should all matplotlib figures be automatically closed after each cell is
989 run?
999 run?
990 """
1000 """
991 from ipykernel.pylab.config import InlineBackend
1001 from ipykernel.pylab.config import InlineBackend
992 cfg = InlineBackend.instance()
1002 cfg = InlineBackend.instance()
993 cfg.close_figures = close
1003 cfg.close_figures = close
994
1004
@@ -1,176 +1,191 b''
1 # Copyright (c) IPython Development Team.
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
2 # Distributed under the terms of the Modified BSD License.
3
3
4 import json
4 import json
5 import tempfile
5 import tempfile
6 import os
6 import os
7 import warnings
7 import warnings
8
8
9 import nose.tools as nt
9 import nose.tools as nt
10
10
11 from IPython.core import display
11 from IPython.core import display
12 from IPython.core.getipython import get_ipython
12 from IPython.core.getipython import get_ipython
13 from IPython import paths as ipath
13 from IPython import paths as ipath
14
14
15 import IPython.testing.decorators as dec
15 import IPython.testing.decorators as dec
16
16
17 def test_image_size():
17 def test_image_size():
18 """Simple test for display.Image(args, width=x,height=y)"""
18 """Simple test for display.Image(args, width=x,height=y)"""
19 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
19 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
20 img = display.Image(url=thisurl, width=200, height=200)
20 img = display.Image(url=thisurl, width=200, height=200)
21 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
21 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
22 img = display.Image(url=thisurl, width=200)
22 img = display.Image(url=thisurl, width=200)
23 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
23 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
24 img = display.Image(url=thisurl)
24 img = display.Image(url=thisurl)
25 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
25 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
26 img = display.Image(url=thisurl, unconfined=True)
26 img = display.Image(url=thisurl, unconfined=True)
27 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
27 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
28
28
29 def test_retina_png():
29 def test_retina_png():
30 here = os.path.dirname(__file__)
30 here = os.path.dirname(__file__)
31 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
31 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
32 nt.assert_equal(img.height, 1)
32 nt.assert_equal(img.height, 1)
33 nt.assert_equal(img.width, 1)
33 nt.assert_equal(img.width, 1)
34 data, md = img._repr_png_()
34 data, md = img._repr_png_()
35 nt.assert_equal(md['width'], 1)
35 nt.assert_equal(md['width'], 1)
36 nt.assert_equal(md['height'], 1)
36 nt.assert_equal(md['height'], 1)
37
37
38 def test_retina_jpeg():
38 def test_retina_jpeg():
39 here = os.path.dirname(__file__)
39 here = os.path.dirname(__file__)
40 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
40 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
41 nt.assert_equal(img.height, 1)
41 nt.assert_equal(img.height, 1)
42 nt.assert_equal(img.width, 1)
42 nt.assert_equal(img.width, 1)
43 data, md = img._repr_jpeg_()
43 data, md = img._repr_jpeg_()
44 nt.assert_equal(md['width'], 1)
44 nt.assert_equal(md['width'], 1)
45 nt.assert_equal(md['height'], 1)
45 nt.assert_equal(md['height'], 1)
46
46
47 def test_base64image():
47 def test_base64image():
48 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
48 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
49
49
50 def test_image_filename_defaults():
50 def test_image_filename_defaults():
51 '''test format constraint, and validity of jpeg and png'''
51 '''test format constraint, and validity of jpeg and png'''
52 tpath = ipath.get_ipython_package_dir()
52 tpath = ipath.get_ipython_package_dir()
53 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
53 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
54 embed=True)
54 embed=True)
55 nt.assert_raises(ValueError, display.Image)
55 nt.assert_raises(ValueError, display.Image)
56 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
56 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
57 # check boths paths to allow packages to test at build and install time
57 # check boths paths to allow packages to test at build and install time
58 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
58 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
59 img = display.Image(filename=imgfile)
59 img = display.Image(filename=imgfile)
60 nt.assert_equal('png', img.format)
60 nt.assert_equal('png', img.format)
61 nt.assert_is_not_none(img._repr_png_())
61 nt.assert_is_not_none(img._repr_png_())
62 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
62 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
63 nt.assert_equal('jpeg', img.format)
63 nt.assert_equal('jpeg', img.format)
64 nt.assert_is_none(img._repr_jpeg_())
64 nt.assert_is_none(img._repr_jpeg_())
65
65
66 def _get_inline_config():
66 def _get_inline_config():
67 from ipykernel.pylab.config import InlineBackend
67 from ipykernel.pylab.config import InlineBackend
68 return InlineBackend.instance()
68 return InlineBackend.instance()
69
69
70 @dec.skip_without('matplotlib')
70 @dec.skip_without('matplotlib')
71 def test_set_matplotlib_close():
71 def test_set_matplotlib_close():
72 cfg = _get_inline_config()
72 cfg = _get_inline_config()
73 cfg.close_figures = False
73 cfg.close_figures = False
74 display.set_matplotlib_close()
74 display.set_matplotlib_close()
75 assert cfg.close_figures
75 assert cfg.close_figures
76 display.set_matplotlib_close(False)
76 display.set_matplotlib_close(False)
77 assert not cfg.close_figures
77 assert not cfg.close_figures
78
78
79 _fmt_mime_map = {
79 _fmt_mime_map = {
80 'png': 'image/png',
80 'png': 'image/png',
81 'jpeg': 'image/jpeg',
81 'jpeg': 'image/jpeg',
82 'pdf': 'application/pdf',
82 'pdf': 'application/pdf',
83 'retina': 'image/png',
83 'retina': 'image/png',
84 'svg': 'image/svg+xml',
84 'svg': 'image/svg+xml',
85 }
85 }
86
86
87 @dec.skip_without('matplotlib')
87 @dec.skip_without('matplotlib')
88 def test_set_matplotlib_formats():
88 def test_set_matplotlib_formats():
89 from matplotlib.figure import Figure
89 from matplotlib.figure import Figure
90 formatters = get_ipython().display_formatter.formatters
90 formatters = get_ipython().display_formatter.formatters
91 for formats in [
91 for formats in [
92 ('png',),
92 ('png',),
93 ('pdf', 'svg'),
93 ('pdf', 'svg'),
94 ('jpeg', 'retina', 'png'),
94 ('jpeg', 'retina', 'png'),
95 (),
95 (),
96 ]:
96 ]:
97 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
97 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
98 display.set_matplotlib_formats(*formats)
98 display.set_matplotlib_formats(*formats)
99 for mime, f in formatters.items():
99 for mime, f in formatters.items():
100 if mime in active_mimes:
100 if mime in active_mimes:
101 nt.assert_in(Figure, f)
101 nt.assert_in(Figure, f)
102 else:
102 else:
103 nt.assert_not_in(Figure, f)
103 nt.assert_not_in(Figure, f)
104
104
105 @dec.skip_without('matplotlib')
105 @dec.skip_without('matplotlib')
106 def test_set_matplotlib_formats_kwargs():
106 def test_set_matplotlib_formats_kwargs():
107 from matplotlib.figure import Figure
107 from matplotlib.figure import Figure
108 ip = get_ipython()
108 ip = get_ipython()
109 cfg = _get_inline_config()
109 cfg = _get_inline_config()
110 cfg.print_figure_kwargs.update(dict(foo='bar'))
110 cfg.print_figure_kwargs.update(dict(foo='bar'))
111 kwargs = dict(quality=10)
111 kwargs = dict(quality=10)
112 display.set_matplotlib_formats('png', **kwargs)
112 display.set_matplotlib_formats('png', **kwargs)
113 formatter = ip.display_formatter.formatters['image/png']
113 formatter = ip.display_formatter.formatters['image/png']
114 f = formatter.lookup_by_type(Figure)
114 f = formatter.lookup_by_type(Figure)
115 cell = f.__closure__[0].cell_contents
115 cell = f.__closure__[0].cell_contents
116 expected = kwargs
116 expected = kwargs
117 expected.update(cfg.print_figure_kwargs)
117 expected.update(cfg.print_figure_kwargs)
118 nt.assert_equal(cell, expected)
118 nt.assert_equal(cell, expected)
119
119
120 def test_displayobject_repr():
120 def test_displayobject_repr():
121 h = display.HTML('<br />')
121 h = display.HTML('<br />')
122 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
122 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
123 h._show_mem_addr = True
123 h._show_mem_addr = True
124 nt.assert_equal(repr(h), object.__repr__(h))
124 nt.assert_equal(repr(h), object.__repr__(h))
125 h._show_mem_addr = False
125 h._show_mem_addr = False
126 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
126 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
127
127
128 j = display.Javascript('')
128 j = display.Javascript('')
129 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
129 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
130 j._show_mem_addr = True
130 j._show_mem_addr = True
131 nt.assert_equal(repr(j), object.__repr__(j))
131 nt.assert_equal(repr(j), object.__repr__(j))
132 j._show_mem_addr = False
132 j._show_mem_addr = False
133 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
133 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
134
134
135 def test_json():
135 def test_json():
136 d = {'a': 5}
136 d = {'a': 5}
137 lis = [d]
137 lis = [d]
138 j = display.JSON(d)
138 j = display.JSON(d)
139 nt.assert_equal(j._repr_json_(), d)
139 nt.assert_equal(j._repr_json_(), d)
140
140
141 with warnings.catch_warnings(record=True) as w:
141 with warnings.catch_warnings(record=True) as w:
142 warnings.simplefilter("always")
142 warnings.simplefilter("always")
143 j = display.JSON(json.dumps(d))
143 j = display.JSON(json.dumps(d))
144 nt.assert_equal(len(w), 1)
144 nt.assert_equal(len(w), 1)
145 nt.assert_equal(j._repr_json_(), d)
145 nt.assert_equal(j._repr_json_(), d)
146
146
147 j = display.JSON(lis)
147 j = display.JSON(lis)
148 nt.assert_equal(j._repr_json_(), lis)
148 nt.assert_equal(j._repr_json_(), lis)
149
149
150 with warnings.catch_warnings(record=True) as w:
150 with warnings.catch_warnings(record=True) as w:
151 warnings.simplefilter("always")
151 warnings.simplefilter("always")
152 j = display.JSON(json.dumps(lis))
152 j = display.JSON(json.dumps(lis))
153 nt.assert_equal(len(w), 1)
153 nt.assert_equal(len(w), 1)
154 nt.assert_equal(j._repr_json_(), lis)
154 nt.assert_equal(j._repr_json_(), lis)
155
155
156 def test_video_embedding():
156 def test_video_embedding():
157 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
157 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
158 v = display.Video("http://ignored")
159 assert not v.embed
160 html = v._repr_html_()
161 nt.assert_not_in('src="data:', html)
162 nt.assert_in('src="http://ignored"', html)
163
164 with nt.assert_raises(ValueError):
165 v = display.Video(b'abc')
166
158 with tempfile.NamedTemporaryFile(suffix='.mp4') as f:
167 with tempfile.NamedTemporaryFile(suffix='.mp4') as f:
159 with open(f.name,'wb') as f:
168 with open(f.name,'wb') as f:
160 f.write(b'abc')
169 f.write(b'abc')
161
170
171 v = display.Video(f.name)
172 assert not v.embed
173 html = v._repr_html_()
174 nt.assert_not_in('src="data:', html)
175
162 v = display.Video(f.name, embed=True)
176 v = display.Video(f.name, embed=True)
163 html = v._repr_html_()
177 html = v._repr_html_()
164 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
178 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
165
179
166 v = display.Video(f.name, embed=True, mimetype='video/other')
180 v = display.Video(f.name, embed=True, mimetype='video/other')
167 html = v._repr_html_()
181 html = v._repr_html_()
168 nt.assert_in('src="data:video/other;base64,YWJj"',html)
182 nt.assert_in('src="data:video/other;base64,YWJj"',html)
169
183
170 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
184 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
171 html = v._repr_html_()
185 html = v._repr_html_()
172 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
186 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
173
187
174 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
188 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
175 html = v._repr_html_()
189 html = v._repr_html_()
176 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
190 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
191
General Comments 0
You need to be logged in to leave comments. Login now