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