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