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