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