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