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