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