##// END OF EJS Templates
xpects typo
MinRK -
Show More
@@ -1,690 +1,690 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2013 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 from __future__ import print_function
21 21
22 22 import os
23 23 import struct
24 24
25 25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 26 unicode_type)
27 27
28 28 from .displaypub import publish_display_data
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # utility functions
32 32 #-----------------------------------------------------------------------------
33 33
34 34 def _safe_exists(path):
35 35 """Check path, but don't let exceptions raise"""
36 36 try:
37 37 return os.path.exists(path)
38 38 except Exception:
39 39 return False
40 40
41 41 def _merge(d1, d2):
42 42 """Like update, but merges sub-dicts instead of clobbering at the top level.
43 43
44 44 Updates d1 in-place
45 45 """
46 46
47 47 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 48 return d2
49 49 for key, value in d2.items():
50 50 d1[key] = _merge(d1.get(key), value)
51 51 return d1
52 52
53 53 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 54 """internal implementation of all display_foo methods
55 55
56 56 Parameters
57 57 ----------
58 58 mimetype : str
59 59 The mimetype to be published (e.g. 'image/png')
60 60 objs : tuple of objects
61 61 The Python objects to display, or if raw=True raw text data to
62 62 display.
63 63 raw : bool
64 64 Are the data objects raw data or Python objects that need to be
65 65 formatted before display? [default: False]
66 66 metadata : dict (optional)
67 67 Metadata to be associated with the specific mimetype output.
68 68 """
69 69 if metadata:
70 70 metadata = {mimetype: metadata}
71 71 if raw:
72 72 # turn list of pngdata into list of { 'image/png': pngdata }
73 73 objs = [ {mimetype: obj} for obj in objs ]
74 74 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75 75
76 76 #-----------------------------------------------------------------------------
77 77 # Main functions
78 78 #-----------------------------------------------------------------------------
79 79
80 80 def display(*objs, **kwargs):
81 81 """Display a Python object in all frontends.
82 82
83 83 By default all representations will be computed and sent to the frontends.
84 84 Frontends can decide which representation is used and how.
85 85
86 86 Parameters
87 87 ----------
88 88 objs : tuple of objects
89 89 The Python objects to display.
90 90 raw : bool, optional
91 91 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
92 92 or Python objects that need to be formatted before display? [default: False]
93 93 include : list or tuple, optional
94 94 A list of format type strings (MIME types) to include in the
95 95 format data dict. If this is set *only* the format types included
96 96 in this list will be computed.
97 97 exclude : list or tuple, optional
98 98 A list of format type strings (MIME types) to exclude in the format
99 99 data dict. If this is set all format types will be computed,
100 100 except for those included in this argument.
101 101 metadata : dict, optional
102 102 A dictionary of metadata to associate with the output.
103 103 mime-type keys in this dictionary will be associated with the individual
104 104 representation formats, if they exist.
105 105 """
106 106 raw = kwargs.get('raw', False)
107 107 include = kwargs.get('include')
108 108 exclude = kwargs.get('exclude')
109 109 metadata = kwargs.get('metadata')
110 110
111 111 from IPython.core.interactiveshell import InteractiveShell
112 112
113 113 if raw:
114 114 for obj in objs:
115 115 publish_display_data('display', obj, metadata)
116 116 else:
117 117 format = InteractiveShell.instance().display_formatter.format
118 118 for obj in objs:
119 119 format_dict, md_dict = format(obj, include=include, exclude=exclude)
120 120 if metadata:
121 121 # kwarg-specified metadata gets precedence
122 122 _merge(md_dict, metadata)
123 123 publish_display_data('display', format_dict, md_dict)
124 124
125 125
126 126 def display_pretty(*objs, **kwargs):
127 127 """Display the pretty (default) representation of an object.
128 128
129 129 Parameters
130 130 ----------
131 131 objs : tuple of objects
132 132 The Python objects to display, or if raw=True raw text data to
133 133 display.
134 134 raw : bool
135 135 Are the data objects raw data or Python objects that need to be
136 136 formatted before display? [default: False]
137 137 metadata : dict (optional)
138 138 Metadata to be associated with the specific mimetype output.
139 139 """
140 140 _display_mimetype('text/plain', objs, **kwargs)
141 141
142 142
143 143 def display_html(*objs, **kwargs):
144 144 """Display the HTML representation of an object.
145 145
146 146 Parameters
147 147 ----------
148 148 objs : tuple of objects
149 149 The Python objects to display, or if raw=True raw HTML data to
150 150 display.
151 151 raw : bool
152 152 Are the data objects raw data or Python objects that need to be
153 153 formatted before display? [default: False]
154 154 metadata : dict (optional)
155 155 Metadata to be associated with the specific mimetype output.
156 156 """
157 157 _display_mimetype('text/html', objs, **kwargs)
158 158
159 159
160 160 def display_svg(*objs, **kwargs):
161 161 """Display the SVG representation of an object.
162 162
163 163 Parameters
164 164 ----------
165 165 objs : tuple of objects
166 166 The Python objects to display, or if raw=True raw svg data to
167 167 display.
168 168 raw : bool
169 169 Are the data objects raw data or Python objects that need to be
170 170 formatted before display? [default: False]
171 171 metadata : dict (optional)
172 172 Metadata to be associated with the specific mimetype output.
173 173 """
174 174 _display_mimetype('image/svg+xml', objs, **kwargs)
175 175
176 176
177 177 def display_png(*objs, **kwargs):
178 178 """Display the PNG representation of an object.
179 179
180 180 Parameters
181 181 ----------
182 182 objs : tuple of objects
183 183 The Python objects to display, or if raw=True raw png data to
184 184 display.
185 185 raw : bool
186 186 Are the data objects raw data or Python objects that need to be
187 187 formatted before display? [default: False]
188 188 metadata : dict (optional)
189 189 Metadata to be associated with the specific mimetype output.
190 190 """
191 191 _display_mimetype('image/png', objs, **kwargs)
192 192
193 193
194 194 def display_jpeg(*objs, **kwargs):
195 195 """Display the JPEG representation of an object.
196 196
197 197 Parameters
198 198 ----------
199 199 objs : tuple of objects
200 200 The Python objects to display, or if raw=True raw JPEG data to
201 201 display.
202 202 raw : bool
203 203 Are the data objects raw data or Python objects that need to be
204 204 formatted before display? [default: False]
205 205 metadata : dict (optional)
206 206 Metadata to be associated with the specific mimetype output.
207 207 """
208 208 _display_mimetype('image/jpeg', objs, **kwargs)
209 209
210 210
211 211 def display_latex(*objs, **kwargs):
212 212 """Display the LaTeX representation of an object.
213 213
214 214 Parameters
215 215 ----------
216 216 objs : tuple of objects
217 217 The Python objects to display, or if raw=True raw latex data to
218 218 display.
219 219 raw : bool
220 220 Are the data objects raw data or Python objects that need to be
221 221 formatted before display? [default: False]
222 222 metadata : dict (optional)
223 223 Metadata to be associated with the specific mimetype output.
224 224 """
225 225 _display_mimetype('text/latex', objs, **kwargs)
226 226
227 227
228 228 def display_json(*objs, **kwargs):
229 229 """Display the JSON representation of an object.
230 230
231 231 Note that not many frontends support displaying JSON.
232 232
233 233 Parameters
234 234 ----------
235 235 objs : tuple of objects
236 236 The Python objects to display, or if raw=True raw json data to
237 237 display.
238 238 raw : bool
239 239 Are the data objects raw data or Python objects that need to be
240 240 formatted before display? [default: False]
241 241 metadata : dict (optional)
242 242 Metadata to be associated with the specific mimetype output.
243 243 """
244 244 _display_mimetype('application/json', objs, **kwargs)
245 245
246 246
247 247 def display_javascript(*objs, **kwargs):
248 248 """Display the Javascript representation of an object.
249 249
250 250 Parameters
251 251 ----------
252 252 objs : tuple of objects
253 253 The Python objects to display, or if raw=True raw javascript data to
254 254 display.
255 255 raw : bool
256 256 Are the data objects raw data or Python objects that need to be
257 257 formatted before display? [default: False]
258 258 metadata : dict (optional)
259 259 Metadata to be associated with the specific mimetype output.
260 260 """
261 261 _display_mimetype('application/javascript', objs, **kwargs)
262 262
263 263 #-----------------------------------------------------------------------------
264 264 # Smart classes
265 265 #-----------------------------------------------------------------------------
266 266
267 267
268 268 class DisplayObject(object):
269 269 """An object that wraps data to be displayed."""
270 270
271 271 _read_flags = 'r'
272 272
273 273 def __init__(self, data=None, url=None, filename=None):
274 274 """Create a display object given raw data.
275 275
276 276 When this object is returned by an expression or passed to the
277 277 display function, it will result in the data being displayed
278 278 in the frontend. The MIME type of the data should match the
279 279 subclasses used, so the Png subclass should be used for 'image/png'
280 280 data. If the data is a URL, the data will first be downloaded
281 281 and then displayed. If
282 282
283 283 Parameters
284 284 ----------
285 285 data : unicode, str or bytes
286 286 The raw data or a URL or file to load the data from
287 287 url : unicode
288 288 A URL to download the data from.
289 289 filename : unicode
290 290 Path to a local file to load the data from.
291 291 """
292 292 if data is not None and isinstance(data, string_types):
293 293 if data.startswith('http') and url is None:
294 294 url = data
295 295 filename = None
296 296 data = None
297 297 elif _safe_exists(data) and filename is None:
298 298 url = None
299 299 filename = data
300 300 data = None
301 301
302 302 self.data = data
303 303 self.url = url
304 304 self.filename = None if filename is None else unicode_type(filename)
305 305
306 306 self.reload()
307 307 self._check_data()
308 308
309 309 def _check_data(self):
310 310 """Override in subclasses if there's something to check."""
311 311 pass
312 312
313 313 def reload(self):
314 314 """Reload the raw data from file or URL."""
315 315 if self.filename is not None:
316 316 with open(self.filename, self._read_flags) as f:
317 317 self.data = f.read()
318 318 elif self.url is not None:
319 319 try:
320 320 try:
321 321 from urllib.request import urlopen # Py3
322 322 except ImportError:
323 323 from urllib2 import urlopen
324 324 response = urlopen(self.url)
325 325 self.data = response.read()
326 326 # extract encoding from header, if there is one:
327 327 encoding = None
328 328 for sub in response.headers['content-type'].split(';'):
329 329 sub = sub.strip()
330 330 if sub.startswith('charset'):
331 331 encoding = sub.split('=')[-1].strip()
332 332 break
333 333 # decode data, if an encoding was specified
334 334 if encoding:
335 335 self.data = self.data.decode(encoding, 'replace')
336 336 except:
337 337 self.data = None
338 338
339 339 class TextDisplayObject(DisplayObject):
340 340 """Validate that display data is text"""
341 341 def _check_data(self):
342 342 if self.data is not None and not isinstance(self.data, string_types):
343 raise TypeError("%s xpects text, not %r" % (self.__class__.__name__, self.data))
343 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
344 344
345 345 class Pretty(TextDisplayObject):
346 346
347 347 def _repr_pretty_(self):
348 348 return self.data
349 349
350 350
351 351 class HTML(TextDisplayObject):
352 352
353 353 def _repr_html_(self):
354 354 return self.data
355 355
356 356 def __html__(self):
357 357 """
358 358 This method exists to inform other HTML-using modules (e.g. Markupsafe,
359 359 htmltag, etc) that this object is HTML and does not need things like
360 360 special characters (<>&) escaped.
361 361 """
362 362 return self._repr_html_()
363 363
364 364
365 365 class Math(TextDisplayObject):
366 366
367 367 def _repr_latex_(self):
368 368 s = self.data.strip('$')
369 369 return "$$%s$$" % s
370 370
371 371
372 372 class Latex(TextDisplayObject):
373 373
374 374 def _repr_latex_(self):
375 375 return self.data
376 376
377 377
378 378 class SVG(DisplayObject):
379 379
380 380 # wrap data in a property, which extracts the <svg> tag, discarding
381 381 # document headers
382 382 _data = None
383 383
384 384 @property
385 385 def data(self):
386 386 return self._data
387 387
388 388 @data.setter
389 389 def data(self, svg):
390 390 if svg is None:
391 391 self._data = None
392 392 return
393 393 # parse into dom object
394 394 from xml.dom import minidom
395 395 svg = cast_bytes_py2(svg)
396 396 x = minidom.parseString(svg)
397 397 # get svg tag (should be 1)
398 398 found_svg = x.getElementsByTagName('svg')
399 399 if found_svg:
400 400 svg = found_svg[0].toxml()
401 401 else:
402 402 # fallback on the input, trust the user
403 403 # but this is probably an error.
404 404 pass
405 405 svg = cast_unicode(svg)
406 406 self._data = svg
407 407
408 408 def _repr_svg_(self):
409 409 return self.data
410 410
411 411
412 412 class JSON(TextDisplayObject):
413 413
414 414 def _repr_json_(self):
415 415 return self.data
416 416
417 417 css_t = """$("head").append($("<link/>").attr({
418 418 rel: "stylesheet",
419 419 type: "text/css",
420 420 href: "%s"
421 421 }));
422 422 """
423 423
424 424 lib_t1 = """$.getScript("%s", function () {
425 425 """
426 426 lib_t2 = """});
427 427 """
428 428
429 429 class Javascript(TextDisplayObject):
430 430
431 431 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
432 432 """Create a Javascript display object given raw data.
433 433
434 434 When this object is returned by an expression or passed to the
435 435 display function, it will result in the data being displayed
436 436 in the frontend. If the data is a URL, the data will first be
437 437 downloaded and then displayed.
438 438
439 439 In the Notebook, the containing element will be available as `element`,
440 440 and jQuery will be available. The output area starts hidden, so if
441 441 the js appends content to `element` that should be visible, then
442 442 it must call `container.show()` to unhide the area.
443 443
444 444 Parameters
445 445 ----------
446 446 data : unicode, str or bytes
447 447 The Javascript source code or a URL to download it from.
448 448 url : unicode
449 449 A URL to download the data from.
450 450 filename : unicode
451 451 Path to a local file to load the data from.
452 452 lib : list or str
453 453 A sequence of Javascript library URLs to load asynchronously before
454 454 running the source code. The full URLs of the libraries should
455 455 be given. A single Javascript library URL can also be given as a
456 456 string.
457 457 css: : list or str
458 458 A sequence of css files to load before running the source code.
459 459 The full URLs of the css files should be given. A single css URL
460 460 can also be given as a string.
461 461 """
462 462 if isinstance(lib, string_types):
463 463 lib = [lib]
464 464 elif lib is None:
465 465 lib = []
466 466 if isinstance(css, string_types):
467 467 css = [css]
468 468 elif css is None:
469 469 css = []
470 470 if not isinstance(lib, (list,tuple)):
471 471 raise TypeError('expected sequence, got: %r' % lib)
472 472 if not isinstance(css, (list,tuple)):
473 473 raise TypeError('expected sequence, got: %r' % css)
474 474 self.lib = lib
475 475 self.css = css
476 476 super(Javascript, self).__init__(data=data, url=url, filename=filename)
477 477
478 478 def _repr_javascript_(self):
479 479 r = ''
480 480 for c in self.css:
481 481 r += css_t % c
482 482 for l in self.lib:
483 483 r += lib_t1 % l
484 484 r += self.data
485 485 r += lib_t2*len(self.lib)
486 486 return r
487 487
488 488 # constants for identifying png/jpeg data
489 489 _PNG = b'\x89PNG\r\n\x1a\n'
490 490 _JPEG = b'\xff\xd8'
491 491
492 492 def _pngxy(data):
493 493 """read the (width, height) from a PNG header"""
494 494 ihdr = data.index(b'IHDR')
495 495 # next 8 bytes are width/height
496 496 w4h4 = data[ihdr+4:ihdr+12]
497 497 return struct.unpack('>ii', w4h4)
498 498
499 499 def _jpegxy(data):
500 500 """read the (width, height) from a JPEG header"""
501 501 # adapted from http://www.64lines.com/jpeg-width-height
502 502
503 503 idx = 4
504 504 while True:
505 505 block_size = struct.unpack('>H', data[idx:idx+2])[0]
506 506 idx = idx + block_size
507 507 if data[idx:idx+2] == b'\xFF\xC0':
508 508 # found Start of Frame
509 509 iSOF = idx
510 510 break
511 511 else:
512 512 # read another block
513 513 idx += 2
514 514
515 515 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
516 516 return w, h
517 517
518 518 class Image(DisplayObject):
519 519
520 520 _read_flags = 'rb'
521 521 _FMT_JPEG = u'jpeg'
522 522 _FMT_PNG = u'png'
523 523 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
524 524
525 525 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
526 526 """Create a PNG/JPEG image object given raw data.
527 527
528 528 When this object is returned by an input cell or passed to the
529 529 display function, it will result in the image being displayed
530 530 in the frontend.
531 531
532 532 Parameters
533 533 ----------
534 534 data : unicode, str or bytes
535 535 The raw image data or a URL or filename to load the data from.
536 536 This always results in embedded image data.
537 537 url : unicode
538 538 A URL to download the data from. If you specify `url=`,
539 539 the image data will not be embedded unless you also specify `embed=True`.
540 540 filename : unicode
541 541 Path to a local file to load the data from.
542 542 Images from a file are always embedded.
543 543 format : unicode
544 544 The format of the image data (png/jpeg/jpg). If a filename or URL is given
545 545 for format will be inferred from the filename extension.
546 546 embed : bool
547 547 Should the image data be embedded using a data URI (True) or be
548 548 loaded using an <img> tag. Set this to True if you want the image
549 549 to be viewable later with no internet connection in the notebook.
550 550
551 551 Default is `True`, unless the keyword argument `url` is set, then
552 552 default value is `False`.
553 553
554 554 Note that QtConsole is not able to display images if `embed` is set to `False`
555 555 width : int
556 556 Width to which to constrain the image in html
557 557 height : int
558 558 Height to which to constrain the image in html
559 559 retina : bool
560 560 Automatically set the width and height to half of the measured
561 561 width and height.
562 562 This only works for embedded images because it reads the width/height
563 563 from image data.
564 564 For non-embedded images, you can just set the desired display width
565 565 and height directly.
566 566
567 567 Examples
568 568 --------
569 569 # embedded image data, works in qtconsole and notebook
570 570 # when passed positionally, the first arg can be any of raw image data,
571 571 # a URL, or a filename from which to load image data.
572 572 # The result is always embedding image data for inline images.
573 573 Image('http://www.google.fr/images/srpr/logo3w.png')
574 574 Image('/path/to/image.jpg')
575 575 Image(b'RAW_PNG_DATA...')
576 576
577 577 # Specifying Image(url=...) does not embed the image data,
578 578 # it only generates `<img>` tag with a link to the source.
579 579 # This will not work in the qtconsole or offline.
580 580 Image(url='http://www.google.fr/images/srpr/logo3w.png')
581 581
582 582 """
583 583 if filename is not None:
584 584 ext = self._find_ext(filename)
585 585 elif url is not None:
586 586 ext = self._find_ext(url)
587 587 elif data is None:
588 588 raise ValueError("No image data found. Expecting filename, url, or data.")
589 589 elif isinstance(data, string_types) and (
590 590 data.startswith('http') or _safe_exists(data)
591 591 ):
592 592 ext = self._find_ext(data)
593 593 else:
594 594 ext = None
595 595
596 596 if ext is not None:
597 597 format = ext.lower()
598 598 if ext == u'jpg' or ext == u'jpeg':
599 599 format = self._FMT_JPEG
600 600 if ext == u'png':
601 601 format = self._FMT_PNG
602 602 elif isinstance(data, bytes) and format == 'png':
603 603 # infer image type from image data header,
604 604 # only if format might not have been specified.
605 605 if data[:2] == _JPEG:
606 606 format = 'jpeg'
607 607
608 608 self.format = unicode_type(format).lower()
609 609 self.embed = embed if embed is not None else (url is None)
610 610
611 611 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
612 612 raise ValueError("Cannot embed the '%s' image format" % (self.format))
613 613 self.width = width
614 614 self.height = height
615 615 self.retina = retina
616 616 super(Image, self).__init__(data=data, url=url, filename=filename)
617 617
618 618 if retina:
619 619 self._retina_shape()
620 620
621 621 def _retina_shape(self):
622 622 """load pixel-doubled width and height from image data"""
623 623 if not self.embed:
624 624 return
625 625 if self.format == 'png':
626 626 w, h = _pngxy(self.data)
627 627 elif self.format == 'jpeg':
628 628 w, h = _jpegxy(self.data)
629 629 else:
630 630 # retina only supports png
631 631 return
632 632 self.width = w // 2
633 633 self.height = h // 2
634 634
635 635 def reload(self):
636 636 """Reload the raw data from file or URL."""
637 637 if self.embed:
638 638 super(Image,self).reload()
639 639 if self.retina:
640 640 self._retina_shape()
641 641
642 642 def _repr_html_(self):
643 643 if not self.embed:
644 644 width = height = ''
645 645 if self.width:
646 646 width = ' width="%d"' % self.width
647 647 if self.height:
648 648 height = ' height="%d"' % self.height
649 649 return u'<img src="%s"%s%s/>' % (self.url, width, height)
650 650
651 651 def _data_and_metadata(self):
652 652 """shortcut for returning metadata with shape information, if defined"""
653 653 md = {}
654 654 if self.width:
655 655 md['width'] = self.width
656 656 if self.height:
657 657 md['height'] = self.height
658 658 if md:
659 659 return self.data, md
660 660 else:
661 661 return self.data
662 662
663 663 def _repr_png_(self):
664 664 if self.embed and self.format == u'png':
665 665 return self._data_and_metadata()
666 666
667 667 def _repr_jpeg_(self):
668 668 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
669 669 return self._data_and_metadata()
670 670
671 671 def _find_ext(self, s):
672 672 return unicode_type(s.split('.')[-1].lower())
673 673
674 674
675 675 def clear_output(wait=False):
676 676 """Clear the output of the current cell receiving output.
677 677
678 678 Parameters
679 679 ----------
680 680 wait : bool [default: false]
681 681 Wait to clear the output until new output is available to replace it."""
682 682 from IPython.core.interactiveshell import InteractiveShell
683 683 if InteractiveShell.initialized():
684 684 InteractiveShell.instance().display_pub.clear_output(wait)
685 685 else:
686 686 from IPython.utils import io
687 687 print('\033[2K\r', file=io.stdout, end='')
688 688 io.stdout.flush()
689 689 print('\033[2K\r', file=io.stderr, end='')
690 690 io.stderr.flush()
General Comments 0
You need to be logged in to leave comments. Login now