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