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