##// END OF EJS Templates
Update some docstring to conform to numpy docstring format
Matthias Bussonnier -
Show More
@@ -1,1206 +1,1214 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 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 654 metadata: dict
655 655 Specify extra metadata to attach to the json display object.
656 656
657 657 Examples
658 658 --------
659 659
660 660 The following will display an interactive map of Mars with a point of
661 661 interest on frontend that do support GeoJSON display.
662 662
663 663 >>> from IPython.display import GeoJSON
664 664
665 665 >>> GeoJSON(data={
666 666 ... "type": "Feature",
667 667 ... "geometry": {
668 668 ... "type": "Point",
669 669 ... "coordinates": [-81.327, 296.038]
670 670 ... }
671 671 ... },
672 672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
673 673 ... layer_options={
674 674 ... "basemap_id": "celestia_mars-shaded-16k_global",
675 675 ... "attribution" : "Celestia/praesepe",
676 676 ... "minZoom" : 0,
677 677 ... "maxZoom" : 18,
678 678 ... })
679 679 <IPython.core.display.GeoJSON object>
680 680
681 681 In the terminal IPython, you will only see the text representation of
682 682 the GeoJSON object.
683 683
684 684 """
685 685
686 686 super(GeoJSON, self).__init__(*args, **kwargs)
687 687
688 688
689 689 def _ipython_display_(self):
690 690 bundle = {
691 691 'application/geo+json': self.data,
692 692 'text/plain': '<IPython.display.GeoJSON object>'
693 693 }
694 694 metadata = {
695 695 'application/geo+json': self.metadata
696 696 }
697 697 display(bundle, metadata=metadata, raw=True)
698 698
699 699 class Javascript(TextDisplayObject):
700 700
701 701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
702 702 """Create a Javascript display object given raw data.
703 703
704 704 When this object is returned by an expression or passed to the
705 705 display function, it will result in the data being displayed
706 706 in the frontend. If the data is a URL, the data will first be
707 707 downloaded and then displayed.
708 708
709 709 In the Notebook, the containing element will be available as `element`,
710 710 and jQuery will be available. Content appended to `element` will be
711 711 visible in the output area.
712 712
713 713 Parameters
714 714 ----------
715 715 data : unicode, str or bytes
716 716 The Javascript source code or a URL to download it from.
717 717 url : unicode
718 718 A URL to download the data from.
719 719 filename : unicode
720 720 Path to a local file to load the data from.
721 721 lib : list or str
722 722 A sequence of Javascript library URLs to load asynchronously before
723 723 running the source code. The full URLs of the libraries should
724 724 be given. A single Javascript library URL can also be given as a
725 725 string.
726 726 css: : list or str
727 727 A sequence of css files to load before running the source code.
728 728 The full URLs of the css files should be given. A single css URL
729 729 can also be given as a string.
730 730 """
731 731 if isinstance(lib, str):
732 732 lib = [lib]
733 733 elif lib is None:
734 734 lib = []
735 735 if isinstance(css, str):
736 736 css = [css]
737 737 elif css is None:
738 738 css = []
739 739 if not isinstance(lib, (list,tuple)):
740 740 raise TypeError('expected sequence, got: %r' % lib)
741 741 if not isinstance(css, (list,tuple)):
742 742 raise TypeError('expected sequence, got: %r' % css)
743 743 self.lib = lib
744 744 self.css = css
745 745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
746 746
747 747 def _repr_javascript_(self):
748 748 r = ''
749 749 for c in self.css:
750 750 r += _css_t % c
751 751 for l in self.lib:
752 752 r += _lib_t1 % l
753 753 r += self.data
754 754 r += _lib_t2*len(self.lib)
755 755 return r
756 756
757 757 # constants for identifying png/jpeg data
758 758 _PNG = b'\x89PNG\r\n\x1a\n'
759 759 _JPEG = b'\xff\xd8'
760 760
761 761 def _pngxy(data):
762 762 """read the (width, height) from a PNG header"""
763 763 ihdr = data.index(b'IHDR')
764 764 # next 8 bytes are width/height
765 765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
766 766
767 767 def _jpegxy(data):
768 768 """read the (width, height) from a JPEG header"""
769 769 # adapted from http://www.64lines.com/jpeg-width-height
770 770
771 771 idx = 4
772 772 while True:
773 773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
774 774 idx = idx + block_size
775 775 if data[idx:idx+2] == b'\xFF\xC0':
776 776 # found Start of Frame
777 777 iSOF = idx
778 778 break
779 779 else:
780 780 # read another block
781 781 idx += 2
782 782
783 783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
784 784 return w, h
785 785
786 786 def _gifxy(data):
787 787 """read the (width, height) from a GIF header"""
788 788 return struct.unpack('<HH', data[6:10])
789 789
790 790
791 791 class Image(DisplayObject):
792 792
793 793 _read_flags = 'rb'
794 794 _FMT_JPEG = u'jpeg'
795 795 _FMT_PNG = u'png'
796 796 _FMT_GIF = u'gif'
797 797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
798 798 _MIMETYPES = {
799 799 _FMT_PNG: 'image/png',
800 800 _FMT_JPEG: 'image/jpeg',
801 801 _FMT_GIF: 'image/gif',
802 802 }
803 803
804 804 def __init__(self, data=None, url=None, filename=None, format=None,
805 805 embed=None, width=None, height=None, retina=False,
806 806 unconfined=False, metadata=None):
807 807 """Create a PNG/JPEG/GIF image object given raw data.
808 808
809 809 When this object is returned by an input cell or passed to the
810 810 display function, it will result in the image being displayed
811 811 in the frontend.
812 812
813 813 Parameters
814 814 ----------
815 815 data : unicode, str or bytes
816 816 The raw image data or a URL or filename to load the data from.
817 817 This always results in embedded image data.
818 818 url : unicode
819 819 A URL to download the data from. If you specify `url=`,
820 820 the image data will not be embedded unless you also specify `embed=True`.
821 821 filename : unicode
822 822 Path to a local file to load the data from.
823 823 Images from a file are always embedded.
824 824 format : unicode
825 825 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
826 826 for format will be inferred from the filename extension.
827 827 embed : bool
828 828 Should the image data be embedded using a data URI (True) or be
829 829 loaded using an <img> tag. Set this to True if you want the image
830 830 to be viewable later with no internet connection in the notebook.
831 831
832 832 Default is `True`, unless the keyword argument `url` is set, then
833 833 default value is `False`.
834 834
835 835 Note that QtConsole is not able to display images if `embed` is set to `False`
836 836 width : int
837 837 Width in pixels to which to constrain the image in html
838 838 height : int
839 839 Height in pixels to which to constrain the image in html
840 840 retina : bool
841 841 Automatically set the width and height to half of the measured
842 842 width and height.
843 843 This only works for embedded images because it reads the width/height
844 844 from image data.
845 845 For non-embedded images, you can just set the desired display width
846 846 and height directly.
847 847 unconfined: bool
848 848 Set unconfined=True to disable max-width confinement of the image.
849 849 metadata: dict
850 850 Specify extra metadata to attach to the image.
851 851
852 852 Examples
853 853 --------
854 # embedded image data, works in qtconsole and notebook
855 # when passed positionally, the first arg can be any of raw image data,
856 # a URL, or a filename from which to load image data.
857 # The result is always embedding image data for inline images.
858 Image('http://www.google.fr/images/srpr/logo3w.png')
859 Image('/path/to/image.jpg')
860 Image(b'RAW_PNG_DATA...')
861
862 # Specifying Image(url=...) does not embed the image data,
863 # it only generates `<img>` tag with a link to the source.
864 # This will not work in the qtconsole or offline.
865 Image(url='http://www.google.fr/images/srpr/logo3w.png')
854
855 embedded image data, works in qtconsole and notebook
856 when passed positionally, the first arg can be any of raw image data,
857 a URL, or a filename from which to load image data.
858 The result is always embedding image data for inline images.
859
860 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
861 <IPython.core.display.Image object>
862
863 >>> Image('/path/to/image.jpg')
864 <IPython.core.display.Image object>
865
866 >>> Image(b'RAW_PNG_DATA...')
867 <IPython.core.display.Image object>
868
869 Specifying Image(url=...) does not embed the image data,
870 it only generates ``<img>`` tag with a link to the source.
871 This will not work in the qtconsole or offline.
872
873 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
866 874
867 875 """
868 876 if isinstance(data, (Path, PurePath)):
869 877 data = str(data)
870 878
871 879 if filename is not None:
872 880 ext = self._find_ext(filename)
873 881 elif url is not None:
874 882 ext = self._find_ext(url)
875 883 elif data is None:
876 884 raise ValueError("No image data found. Expecting filename, url, or data.")
877 885 elif isinstance(data, str) and (
878 886 data.startswith('http') or _safe_exists(data)
879 887 ):
880 888 ext = self._find_ext(data)
881 889 else:
882 890 ext = None
883 891
884 892 if format is None:
885 893 if ext is not None:
886 894 if ext == u'jpg' or ext == u'jpeg':
887 895 format = self._FMT_JPEG
888 896 elif ext == u'png':
889 897 format = self._FMT_PNG
890 898 elif ext == u'gif':
891 899 format = self._FMT_GIF
892 900 else:
893 901 format = ext.lower()
894 902 elif isinstance(data, bytes):
895 903 # infer image type from image data header,
896 904 # only if format has not been specified.
897 905 if data[:2] == _JPEG:
898 906 format = self._FMT_JPEG
899 907
900 908 # failed to detect format, default png
901 909 if format is None:
902 910 format = self._FMT_PNG
903 911
904 912 if format.lower() == 'jpg':
905 913 # jpg->jpeg
906 914 format = self._FMT_JPEG
907 915
908 916 self.format = format.lower()
909 917 self.embed = embed if embed is not None else (url is None)
910 918
911 919 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
912 920 raise ValueError("Cannot embed the '%s' image format" % (self.format))
913 921 if self.embed:
914 922 self._mimetype = self._MIMETYPES.get(self.format)
915 923
916 924 self.width = width
917 925 self.height = height
918 926 self.retina = retina
919 927 self.unconfined = unconfined
920 928 super(Image, self).__init__(data=data, url=url, filename=filename,
921 929 metadata=metadata)
922 930
923 931 if self.width is None and self.metadata.get('width', {}):
924 932 self.width = metadata['width']
925 933
926 934 if self.height is None and self.metadata.get('height', {}):
927 935 self.height = metadata['height']
928 936
929 937 if retina:
930 938 self._retina_shape()
931 939
932 940
933 941 def _retina_shape(self):
934 942 """load pixel-doubled width and height from image data"""
935 943 if not self.embed:
936 944 return
937 945 if self.format == self._FMT_PNG:
938 946 w, h = _pngxy(self.data)
939 947 elif self.format == self._FMT_JPEG:
940 948 w, h = _jpegxy(self.data)
941 949 elif self.format == self._FMT_GIF:
942 950 w, h = _gifxy(self.data)
943 951 else:
944 952 # retina only supports png
945 953 return
946 954 self.width = w // 2
947 955 self.height = h // 2
948 956
949 957 def reload(self):
950 958 """Reload the raw data from file or URL."""
951 959 if self.embed:
952 960 super(Image,self).reload()
953 961 if self.retina:
954 962 self._retina_shape()
955 963
956 964 def _repr_html_(self):
957 965 if not self.embed:
958 966 width = height = klass = ''
959 967 if self.width:
960 968 width = ' width="%d"' % self.width
961 969 if self.height:
962 970 height = ' height="%d"' % self.height
963 971 if self.unconfined:
964 972 klass = ' class="unconfined"'
965 973 return u'<img src="{url}"{width}{height}{klass}/>'.format(
966 974 url=self.url,
967 975 width=width,
968 976 height=height,
969 977 klass=klass,
970 978 )
971 979
972 980 def _repr_mimebundle_(self, include=None, exclude=None):
973 981 """Return the image as a mimebundle
974 982
975 983 Any new mimetype support should be implemented here.
976 984 """
977 985 if self.embed:
978 986 mimetype = self._mimetype
979 987 data, metadata = self._data_and_metadata(always_both=True)
980 988 if metadata:
981 989 metadata = {mimetype: metadata}
982 990 return {mimetype: data}, metadata
983 991 else:
984 992 return {'text/html': self._repr_html_()}
985 993
986 994 def _data_and_metadata(self, always_both=False):
987 995 """shortcut for returning metadata with shape information, if defined"""
988 996 try:
989 997 b64_data = b2a_base64(self.data).decode('ascii')
990 998 except TypeError as e:
991 999 raise FileNotFoundError(
992 1000 "No such file or directory: '%s'" % (self.data)) from e
993 1001 md = {}
994 1002 if self.metadata:
995 1003 md.update(self.metadata)
996 1004 if self.width:
997 1005 md['width'] = self.width
998 1006 if self.height:
999 1007 md['height'] = self.height
1000 1008 if self.unconfined:
1001 1009 md['unconfined'] = self.unconfined
1002 1010 if md or always_both:
1003 1011 return b64_data, md
1004 1012 else:
1005 1013 return b64_data
1006 1014
1007 1015 def _repr_png_(self):
1008 1016 if self.embed and self.format == self._FMT_PNG:
1009 1017 return self._data_and_metadata()
1010 1018
1011 1019 def _repr_jpeg_(self):
1012 1020 if self.embed and self.format == self._FMT_JPEG:
1013 1021 return self._data_and_metadata()
1014 1022
1015 1023 def _find_ext(self, s):
1016 1024 base, ext = splitext(s)
1017 1025
1018 1026 if not ext:
1019 1027 return base
1020 1028
1021 1029 # `splitext` includes leading period, so we skip it
1022 1030 return ext[1:].lower()
1023 1031
1024 1032
1025 1033 class Video(DisplayObject):
1026 1034
1027 1035 def __init__(self, data=None, url=None, filename=None, embed=False,
1028 1036 mimetype=None, width=None, height=None, html_attributes="controls"):
1029 1037 """Create a video object given raw data or an URL.
1030 1038
1031 1039 When this object is returned by an input cell or passed to the
1032 1040 display function, it will result in the video being displayed
1033 1041 in the frontend.
1034 1042
1035 1043 Parameters
1036 1044 ----------
1037 1045 data : unicode, str or bytes
1038 1046 The raw video data or a URL or filename to load the data from.
1039 1047 Raw data will require passing `embed=True`.
1040 1048 url : unicode
1041 1049 A URL for the video. If you specify `url=`,
1042 1050 the image data will not be embedded.
1043 1051 filename : unicode
1044 1052 Path to a local file containing the video.
1045 1053 Will be interpreted as a local URL unless `embed=True`.
1046 1054 embed : bool
1047 1055 Should the video be embedded using a data URI (True) or be
1048 1056 loaded using a <video> tag (False).
1049 1057
1050 1058 Since videos are large, embedding them should be avoided, if possible.
1051 1059 You must confirm embedding as your intention by passing `embed=True`.
1052 1060
1053 1061 Local files can be displayed with URLs without embedding the content, via::
1054 1062
1055 1063 Video('./video.mp4')
1056 1064
1057 1065 mimetype: unicode
1058 1066 Specify the mimetype for embedded videos.
1059 1067 Default will be guessed from file extension, if available.
1060 1068 width : int
1061 1069 Width in pixels to which to constrain the video in HTML.
1062 1070 If not supplied, defaults to the width of the video.
1063 1071 height : int
1064 1072 Height in pixels to which to constrain the video in html.
1065 1073 If not supplied, defaults to the height of the video.
1066 1074 html_attributes : str
1067 1075 Attributes for the HTML `<video>` block.
1068 1076 Default: `"controls"` to get video controls.
1069 1077 Other examples: `"controls muted"` for muted video with controls,
1070 1078 `"loop autoplay"` for looping autoplaying video without controls.
1071 1079
1072 1080 Examples
1073 1081 --------
1074 1082
1075 1083 ::
1076 1084
1077 1085 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1078 1086 Video('path/to/video.mp4')
1079 1087 Video('path/to/video.mp4', embed=True)
1080 1088 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1081 1089 Video(b'raw-videodata', embed=True)
1082 1090 """
1083 1091 if isinstance(data, (Path, PurePath)):
1084 1092 data = str(data)
1085 1093
1086 1094 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1087 1095 url = data
1088 1096 data = None
1089 1097 elif data is not None and os.path.exists(data):
1090 1098 filename = data
1091 1099 data = None
1092 1100
1093 1101 if data and not embed:
1094 1102 msg = ''.join([
1095 1103 "To embed videos, you must pass embed=True ",
1096 1104 "(this may make your notebook files huge)\n",
1097 1105 "Consider passing Video(url='...')",
1098 1106 ])
1099 1107 raise ValueError(msg)
1100 1108
1101 1109 self.mimetype = mimetype
1102 1110 self.embed = embed
1103 1111 self.width = width
1104 1112 self.height = height
1105 1113 self.html_attributes = html_attributes
1106 1114 super(Video, self).__init__(data=data, url=url, filename=filename)
1107 1115
1108 1116 def _repr_html_(self):
1109 1117 width = height = ''
1110 1118 if self.width:
1111 1119 width = ' width="%d"' % self.width
1112 1120 if self.height:
1113 1121 height = ' height="%d"' % self.height
1114 1122
1115 1123 # External URLs and potentially local files are not embedded into the
1116 1124 # notebook output.
1117 1125 if not self.embed:
1118 1126 url = self.url if self.url is not None else self.filename
1119 1127 output = """<video src="{0}" {1} {2} {3}>
1120 1128 Your browser does not support the <code>video</code> element.
1121 1129 </video>""".format(url, self.html_attributes, width, height)
1122 1130 return output
1123 1131
1124 1132 # Embedded videos are base64-encoded.
1125 1133 mimetype = self.mimetype
1126 1134 if self.filename is not None:
1127 1135 if not mimetype:
1128 1136 mimetype, _ = mimetypes.guess_type(self.filename)
1129 1137
1130 1138 with open(self.filename, 'rb') as f:
1131 1139 video = f.read()
1132 1140 else:
1133 1141 video = self.data
1134 1142 if isinstance(video, str):
1135 1143 # unicode input is already b64-encoded
1136 1144 b64_video = video
1137 1145 else:
1138 1146 b64_video = b2a_base64(video).decode('ascii').rstrip()
1139 1147
1140 1148 output = """<video {0} {1} {2}>
1141 1149 <source src="data:{3};base64,{4}" type="{3}">
1142 1150 Your browser does not support the video tag.
1143 1151 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1144 1152 return output
1145 1153
1146 1154 def reload(self):
1147 1155 # TODO
1148 1156 pass
1149 1157
1150 1158
1151 1159 @skip_doctest
1152 1160 def set_matplotlib_formats(*formats, **kwargs):
1153 1161 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1154 1162
1155 1163 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1156 1164
1157 1165 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1158 1166
1159 1167 To set this in your config files use the following::
1160 1168
1161 1169 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1162 1170 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1163 1171
1164 1172 Parameters
1165 1173 ----------
1166 1174 *formats : strs
1167 1175 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1168 1176 **kwargs :
1169 1177 Keyword args will be relayed to ``figure.canvas.print_figure``.
1170 1178 """
1171 1179 from IPython.core.interactiveshell import InteractiveShell
1172 1180 from IPython.core.pylabtools import select_figure_formats
1173 1181 # build kwargs, starting with InlineBackend config
1174 1182 kw = {}
1175 1183 from ipykernel.pylab.config import InlineBackend
1176 1184 cfg = InlineBackend.instance()
1177 1185 kw.update(cfg.print_figure_kwargs)
1178 1186 kw.update(**kwargs)
1179 1187 shell = InteractiveShell.instance()
1180 1188 select_figure_formats(shell, formats, **kw)
1181 1189
1182 1190 @skip_doctest
1183 1191 def set_matplotlib_close(close=True):
1184 1192 """Set whether the inline backend closes all figures automatically or not.
1185 1193
1186 1194 By default, the inline backend used in the IPython Notebook will close all
1187 1195 matplotlib figures automatically after each cell is run. This means that
1188 1196 plots in different cells won't interfere. Sometimes, you may want to make
1189 1197 a plot in one cell and then refine it in later cells. This can be accomplished
1190 1198 by::
1191 1199
1192 1200 In [1]: set_matplotlib_close(False)
1193 1201
1194 1202 To set this in your config files use the following::
1195 1203
1196 1204 c.InlineBackend.close_figures = False
1197 1205
1198 1206 Parameters
1199 1207 ----------
1200 1208 close : bool
1201 1209 Should all matplotlib figures be automatically closed after each cell is
1202 1210 run?
1203 1211 """
1204 1212 from ipykernel.pylab.config import InlineBackend
1205 1213 cfg = InlineBackend.instance()
1206 1214 cfg.close_figures = close
@@ -1,659 +1,659 b''
1 1 """Various display related classes.
2 2
3 3 Authors : MinRK, gregcaporaso, dannystaple
4 4 """
5 5 from html import escape as html_escape
6 6 from os.path import exists, isfile, splitext, abspath, join, isdir
7 7 from os import walk, sep, fsdecode
8 8
9 9 from IPython.core.display import DisplayObject, TextDisplayObject
10 10
11 11 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
12 12 'FileLink', 'FileLinks', 'Code']
13 13
14 14
15 15 class Audio(DisplayObject):
16 16 """Create an audio object.
17 17
18 18 When this object is returned by an input cell or passed to the
19 19 display function, it will result in Audio controls being displayed
20 20 in the frontend (only works in the notebook).
21 21
22 22 Parameters
23 23 ----------
24 24 data : numpy array, list, unicode, str or bytes
25 25 Can be one of
26 26
27 27 * Numpy 1d array containing the desired waveform (mono)
28 28 * Numpy 2d array containing waveforms for each channel.
29 29 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
30 30 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
31 31 * List of float or integer representing the waveform (mono)
32 32 * String containing the filename
33 33 * Bytestring containing raw PCM data or
34 34 * URL pointing to a file on the web.
35 35
36 36 If the array option is used, the waveform will be normalized.
37 37
38 38 If a filename or url is used, the format support will be browser
39 39 dependent.
40 40 url : unicode
41 41 A URL to download the data from.
42 42 filename : unicode
43 43 Path to a local file to load the data from.
44 44 embed : boolean
45 45 Should the audio data be embedded using a data URI (True) or should
46 46 the original source be referenced. Set this to True if you want the
47 47 audio to playable later with no internet connection in the notebook.
48 48
49 49 Default is `True`, unless the keyword argument `url` is set, then
50 50 default value is `False`.
51 51 rate : integer
52 52 The sampling rate of the raw data.
53 53 Only required when data parameter is being used as an array
54 54 autoplay : bool
55 55 Set to True if the audio should immediately start playing.
56 56 Default is `False`.
57 57 normalize : bool
58 58 Whether audio should be normalized (rescaled) to the maximum possible
59 59 range. Default is `True`. When set to `False`, `data` must be between
60 60 -1 and 1 (inclusive), otherwise an error is raised.
61 61 Applies only when `data` is a list or array of samples; other types of
62 62 audio are never normalized.
63 63
64 64 Examples
65 65 --------
66 66
67 67 Generate a sound
68 68
69 69 >>> import numpy as np
70 70 ... framerate = 44100
71 71 ... t = np.linspace(0,5,framerate*5)
72 72 ... data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
73 ... Audio(data,rate=framerate)
73 ... Audio(data, rate=framerate)
74 74
75 75 Can also do stereo or more channels
76 76
77 77 >>> dataleft = np.sin(2*np.pi*220*t)
78 78 ... dataright = np.sin(2*np.pi*224*t)
79 ... Audio([dataleft, dataright],rate=framerate)
79 ... Audio([dataleft, dataright], rate=framerate)
80 80
81 81 From URL:
82 82
83 83 >>> Audio("http://www.nch.com.au/acm/8k16bitpcm.wav")
84 84 >>> Audio(url="http://www.w3schools.com/html/horse.ogg")
85 85
86 86 From a File:
87 87
88 88 >>> Audio('/path/to/sound.wav')
89 89 >>> Audio(filename='/path/to/sound.ogg')
90 90
91 91 From Bytes:
92 92
93 93 >>> Audio(b'RAW_WAV_DATA..')
94 94 >>> Audio(data=b'RAW_WAV_DATA..')
95 95
96 96 See Also
97 97 --------
98 98 ipywidgets.Audio
99 99
100 100 AUdio widget with more more flexibility and options.
101 101
102 102 """
103 103 _read_flags = 'rb'
104 104
105 105 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
106 106 element_id=None):
107 107 if filename is None and url is None and data is None:
108 108 raise ValueError("No audio data found. Expecting filename, url, or data.")
109 109 if embed is False and url is None:
110 110 raise ValueError("No url found. Expecting url when embed=False")
111 111
112 112 if url is not None and embed is not True:
113 113 self.embed = False
114 114 else:
115 115 self.embed = True
116 116 self.autoplay = autoplay
117 117 self.element_id = element_id
118 118 super(Audio, self).__init__(data=data, url=url, filename=filename)
119 119
120 120 if self.data is not None and not isinstance(self.data, bytes):
121 121 if rate is None:
122 122 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
123 123 self.data = Audio._make_wav(data, rate, normalize)
124 124
125 125 def reload(self):
126 126 """Reload the raw data from file or URL."""
127 127 import mimetypes
128 128 if self.embed:
129 129 super(Audio, self).reload()
130 130
131 131 if self.filename is not None:
132 132 self.mimetype = mimetypes.guess_type(self.filename)[0]
133 133 elif self.url is not None:
134 134 self.mimetype = mimetypes.guess_type(self.url)[0]
135 135 else:
136 136 self.mimetype = "audio/wav"
137 137
138 138 @staticmethod
139 139 def _make_wav(data, rate, normalize):
140 140 """ Transform a numpy array to a PCM bytestring """
141 141 from io import BytesIO
142 142 import wave
143 143
144 144 try:
145 145 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
146 146 except ImportError:
147 147 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
148 148
149 149 fp = BytesIO()
150 150 waveobj = wave.open(fp,mode='wb')
151 151 waveobj.setnchannels(nchan)
152 152 waveobj.setframerate(rate)
153 153 waveobj.setsampwidth(2)
154 154 waveobj.setcomptype('NONE','NONE')
155 155 waveobj.writeframes(scaled)
156 156 val = fp.getvalue()
157 157 waveobj.close()
158 158
159 159 return val
160 160
161 161 @staticmethod
162 162 def _validate_and_normalize_with_numpy(data, normalize):
163 163 import numpy as np
164 164
165 165 data = np.array(data, dtype=float)
166 166 if len(data.shape) == 1:
167 167 nchan = 1
168 168 elif len(data.shape) == 2:
169 169 # In wave files,channels are interleaved. E.g.,
170 170 # "L1R1L2R2..." for stereo. See
171 171 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
172 172 # for channel ordering
173 173 nchan = data.shape[0]
174 174 data = data.T.ravel()
175 175 else:
176 176 raise ValueError('Array audio input must be a 1D or 2D array')
177 177
178 178 max_abs_value = np.max(np.abs(data))
179 179 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
180 180 scaled = data / normalization_factor * 32767
181 181 return scaled.astype('<h').tostring(), nchan
182 182
183 183
184 184 @staticmethod
185 185 def _validate_and_normalize_without_numpy(data, normalize):
186 186 import array
187 187 import sys
188 188
189 189 data = array.array('f', data)
190 190
191 191 try:
192 192 max_abs_value = float(max([abs(x) for x in data]))
193 193 except TypeError as e:
194 194 raise TypeError('Only lists of mono audio are '
195 195 'supported if numpy is not installed') from e
196 196
197 197 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
198 198 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
199 199 if sys.byteorder == 'big':
200 200 scaled.byteswap()
201 201 nchan = 1
202 202 return scaled.tobytes(), nchan
203 203
204 204 @staticmethod
205 205 def _get_normalization_factor(max_abs_value, normalize):
206 206 if not normalize and max_abs_value > 1:
207 207 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
208 208 return max_abs_value if normalize else 1
209 209
210 210 def _data_and_metadata(self):
211 211 """shortcut for returning metadata with url information, if defined"""
212 212 md = {}
213 213 if self.url:
214 214 md['url'] = self.url
215 215 if md:
216 216 return self.data, md
217 217 else:
218 218 return self.data
219 219
220 220 def _repr_html_(self):
221 221 src = """
222 222 <audio {element_id} controls="controls" {autoplay}>
223 223 <source src="{src}" type="{type}" />
224 224 Your browser does not support the audio element.
225 225 </audio>
226 226 """
227 227 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
228 228 element_id=self.element_id_attr())
229 229
230 230 def src_attr(self):
231 231 import base64
232 232 if self.embed and (self.data is not None):
233 233 data = base64=base64.b64encode(self.data).decode('ascii')
234 234 return """data:{type};base64,{base64}""".format(type=self.mimetype,
235 235 base64=data)
236 236 elif self.url is not None:
237 237 return self.url
238 238 else:
239 239 return ""
240 240
241 241 def autoplay_attr(self):
242 242 if(self.autoplay):
243 243 return 'autoplay="autoplay"'
244 244 else:
245 245 return ''
246 246
247 247 def element_id_attr(self):
248 248 if (self.element_id):
249 249 return 'id="{element_id}"'.format(element_id=self.element_id)
250 250 else:
251 251 return ''
252 252
253 253 class IFrame(object):
254 254 """
255 255 Generic class to embed an iframe in an IPython notebook
256 256 """
257 257
258 258 iframe = """
259 259 <iframe
260 260 width="{width}"
261 261 height="{height}"
262 262 src="{src}{params}"
263 263 frameborder="0"
264 264 allowfullscreen
265 265 ></iframe>
266 266 """
267 267
268 268 def __init__(self, src, width, height, **kwargs):
269 269 self.src = src
270 270 self.width = width
271 271 self.height = height
272 272 self.params = kwargs
273 273
274 274 def _repr_html_(self):
275 275 """return the embed iframe"""
276 276 if self.params:
277 277 from urllib.parse import urlencode
278 278 params = "?" + urlencode(self.params)
279 279 else:
280 280 params = ""
281 281 return self.iframe.format(src=self.src,
282 282 width=self.width,
283 283 height=self.height,
284 284 params=params)
285 285
286 286 class YouTubeVideo(IFrame):
287 287 """Class for embedding a YouTube Video in an IPython session, based on its video id.
288 288
289 289 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
290 290 do::
291 291
292 292 vid = YouTubeVideo("foo")
293 293 display(vid)
294 294
295 295 To start from 30 seconds::
296 296
297 297 vid = YouTubeVideo("abc", start=30)
298 298 display(vid)
299 299
300 300 To calculate seconds from time as hours, minutes, seconds use
301 301 :class:`datetime.timedelta`::
302 302
303 303 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
304 304
305 305 Other parameters can be provided as documented at
306 306 https://developers.google.com/youtube/player_parameters#Parameters
307 307
308 308 When converting the notebook using nbconvert, a jpeg representation of the video
309 309 will be inserted in the document.
310 310 """
311 311
312 312 def __init__(self, id, width=400, height=300, **kwargs):
313 313 self.id=id
314 314 src = "https://www.youtube.com/embed/{0}".format(id)
315 315 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
316 316
317 317 def _repr_jpeg_(self):
318 318 # Deferred import
319 319 from urllib.request import urlopen
320 320
321 321 try:
322 322 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
323 323 except IOError:
324 324 return None
325 325
326 326 class VimeoVideo(IFrame):
327 327 """
328 328 Class for embedding a Vimeo video in an IPython session, based on its video id.
329 329 """
330 330
331 331 def __init__(self, id, width=400, height=300, **kwargs):
332 332 src="https://player.vimeo.com/video/{0}".format(id)
333 333 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
334 334
335 335 class ScribdDocument(IFrame):
336 336 """
337 337 Class for embedding a Scribd document in an IPython session
338 338
339 339 Use the start_page params to specify a starting point in the document
340 340 Use the view_mode params to specify display type one off scroll | slideshow | book
341 341
342 342 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
343 343
344 344 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
345 345 """
346 346
347 347 def __init__(self, id, width=400, height=300, **kwargs):
348 348 src="https://www.scribd.com/embeds/{0}/content".format(id)
349 349 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
350 350
351 351 class FileLink(object):
352 352 """Class for embedding a local file link in an IPython session, based on path
353 353
354 354 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
355 355
356 356 you would do::
357 357
358 358 local_file = FileLink("my/data.txt")
359 359 display(local_file)
360 360
361 361 or in the HTML notebook, just::
362 362
363 363 FileLink("my/data.txt")
364 364 """
365 365
366 366 html_link_str = "<a href='%s' target='_blank'>%s</a>"
367 367
368 368 def __init__(self,
369 369 path,
370 370 url_prefix='',
371 371 result_html_prefix='',
372 372 result_html_suffix='<br>'):
373 373 """
374 374 Parameters
375 375 ----------
376 376 path : str
377 377 path to the file or directory that should be formatted
378 378 url_prefix : str
379 379 prefix to be prepended to all files to form a working link [default:
380 380 '']
381 381 result_html_prefix : str
382 382 text to append to beginning to link [default: '']
383 383 result_html_suffix : str
384 384 text to append at the end of link [default: '<br>']
385 385 """
386 386 if isdir(path):
387 387 raise ValueError("Cannot display a directory using FileLink. "
388 388 "Use FileLinks to display '%s'." % path)
389 389 self.path = fsdecode(path)
390 390 self.url_prefix = url_prefix
391 391 self.result_html_prefix = result_html_prefix
392 392 self.result_html_suffix = result_html_suffix
393 393
394 394 def _format_path(self):
395 395 fp = ''.join([self.url_prefix, html_escape(self.path)])
396 396 return ''.join([self.result_html_prefix,
397 397 self.html_link_str % \
398 398 (fp, html_escape(self.path, quote=False)),
399 399 self.result_html_suffix])
400 400
401 401 def _repr_html_(self):
402 402 """return html link to file
403 403 """
404 404 if not exists(self.path):
405 405 return ("Path (<tt>%s</tt>) doesn't exist. "
406 406 "It may still be in the process of "
407 407 "being generated, or you may have the "
408 408 "incorrect path." % self.path)
409 409
410 410 return self._format_path()
411 411
412 412 def __repr__(self):
413 413 """return absolute path to file
414 414 """
415 415 return abspath(self.path)
416 416
417 417 class FileLinks(FileLink):
418 418 """Class for embedding local file links in an IPython session, based on path
419 419
420 420 e.g. to embed links to files that were generated in the IPython notebook
421 421 under ``my/data``, you would do::
422 422
423 423 local_files = FileLinks("my/data")
424 424 display(local_files)
425 425
426 426 or in the HTML notebook, just::
427 427
428 428 FileLinks("my/data")
429 429 """
430 430 def __init__(self,
431 431 path,
432 432 url_prefix='',
433 433 included_suffixes=None,
434 434 result_html_prefix='',
435 435 result_html_suffix='<br>',
436 436 notebook_display_formatter=None,
437 437 terminal_display_formatter=None,
438 438 recursive=True):
439 439 """
440 440 See :class:`FileLink` for the ``path``, ``url_prefix``,
441 441 ``result_html_prefix`` and ``result_html_suffix`` parameters.
442 442
443 443 included_suffixes : list
444 444 Filename suffixes to include when formatting output [default: include
445 445 all files]
446 446
447 447 notebook_display_formatter : function
448 448 Used to format links for display in the notebook. See discussion of
449 449 formatter functions below.
450 450
451 451 terminal_display_formatter : function
452 452 Used to format links for display in the terminal. See discussion of
453 453 formatter functions below.
454 454
455 455 Formatter functions must be of the form::
456 456
457 457 f(dirname, fnames, included_suffixes)
458 458
459 459 dirname : str
460 460 The name of a directory
461 461 fnames : list
462 462 The files in that directory
463 463 included_suffixes : list
464 464 The file suffixes that should be included in the output (passing None
465 465 meansto include all suffixes in the output in the built-in formatters)
466 466 recursive : boolean
467 467 Whether to recurse into subdirectories. Default is True.
468 468
469 469 The function should return a list of lines that will be printed in the
470 470 notebook (if passing notebook_display_formatter) or the terminal (if
471 471 passing terminal_display_formatter). This function is iterated over for
472 472 each directory in self.path. Default formatters are in place, can be
473 473 passed here to support alternative formatting.
474 474
475 475 """
476 476 if isfile(path):
477 477 raise ValueError("Cannot display a file using FileLinks. "
478 478 "Use FileLink to display '%s'." % path)
479 479 self.included_suffixes = included_suffixes
480 480 # remove trailing slashes for more consistent output formatting
481 481 path = path.rstrip('/')
482 482
483 483 self.path = path
484 484 self.url_prefix = url_prefix
485 485 self.result_html_prefix = result_html_prefix
486 486 self.result_html_suffix = result_html_suffix
487 487
488 488 self.notebook_display_formatter = \
489 489 notebook_display_formatter or self._get_notebook_display_formatter()
490 490 self.terminal_display_formatter = \
491 491 terminal_display_formatter or self._get_terminal_display_formatter()
492 492
493 493 self.recursive = recursive
494 494
495 495 def _get_display_formatter(self,
496 496 dirname_output_format,
497 497 fname_output_format,
498 498 fp_format,
499 499 fp_cleaner=None):
500 500 """ generate built-in formatter function
501 501
502 502 this is used to define both the notebook and terminal built-in
503 503 formatters as they only differ by some wrapper text for each entry
504 504
505 505 dirname_output_format: string to use for formatting directory
506 506 names, dirname will be substituted for a single "%s" which
507 507 must appear in this string
508 508 fname_output_format: string to use for formatting file names,
509 509 if a single "%s" appears in the string, fname will be substituted
510 510 if two "%s" appear in the string, the path to fname will be
511 511 substituted for the first and fname will be substituted for the
512 512 second
513 513 fp_format: string to use for formatting filepaths, must contain
514 514 exactly two "%s" and the dirname will be substituted for the first
515 515 and fname will be substituted for the second
516 516 """
517 517 def f(dirname, fnames, included_suffixes=None):
518 518 result = []
519 519 # begin by figuring out which filenames, if any,
520 520 # are going to be displayed
521 521 display_fnames = []
522 522 for fname in fnames:
523 523 if (isfile(join(dirname,fname)) and
524 524 (included_suffixes is None or
525 525 splitext(fname)[1] in included_suffixes)):
526 526 display_fnames.append(fname)
527 527
528 528 if len(display_fnames) == 0:
529 529 # if there are no filenames to display, don't print anything
530 530 # (not even the directory name)
531 531 pass
532 532 else:
533 533 # otherwise print the formatted directory name followed by
534 534 # the formatted filenames
535 535 dirname_output_line = dirname_output_format % dirname
536 536 result.append(dirname_output_line)
537 537 for fname in display_fnames:
538 538 fp = fp_format % (dirname,fname)
539 539 if fp_cleaner is not None:
540 540 fp = fp_cleaner(fp)
541 541 try:
542 542 # output can include both a filepath and a filename...
543 543 fname_output_line = fname_output_format % (fp, fname)
544 544 except TypeError:
545 545 # ... or just a single filepath
546 546 fname_output_line = fname_output_format % fname
547 547 result.append(fname_output_line)
548 548 return result
549 549 return f
550 550
551 551 def _get_notebook_display_formatter(self,
552 552 spacer="&nbsp;&nbsp;"):
553 553 """ generate function to use for notebook formatting
554 554 """
555 555 dirname_output_format = \
556 556 self.result_html_prefix + "%s/" + self.result_html_suffix
557 557 fname_output_format = \
558 558 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
559 559 fp_format = self.url_prefix + '%s/%s'
560 560 if sep == "\\":
561 561 # Working on a platform where the path separator is "\", so
562 562 # must convert these to "/" for generating a URI
563 563 def fp_cleaner(fp):
564 564 # Replace all occurrences of backslash ("\") with a forward
565 565 # slash ("/") - this is necessary on windows when a path is
566 566 # provided as input, but we must link to a URI
567 567 return fp.replace('\\','/')
568 568 else:
569 569 fp_cleaner = None
570 570
571 571 return self._get_display_formatter(dirname_output_format,
572 572 fname_output_format,
573 573 fp_format,
574 574 fp_cleaner)
575 575
576 576 def _get_terminal_display_formatter(self,
577 577 spacer=" "):
578 578 """ generate function to use for terminal formatting
579 579 """
580 580 dirname_output_format = "%s/"
581 581 fname_output_format = spacer + "%s"
582 582 fp_format = '%s/%s'
583 583
584 584 return self._get_display_formatter(dirname_output_format,
585 585 fname_output_format,
586 586 fp_format)
587 587
588 588 def _format_path(self):
589 589 result_lines = []
590 590 if self.recursive:
591 591 walked_dir = list(walk(self.path))
592 592 else:
593 593 walked_dir = [next(walk(self.path))]
594 594 walked_dir.sort()
595 595 for dirname, subdirs, fnames in walked_dir:
596 596 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
597 597 return '\n'.join(result_lines)
598 598
599 599 def __repr__(self):
600 600 """return newline-separated absolute paths
601 601 """
602 602 result_lines = []
603 603 if self.recursive:
604 604 walked_dir = list(walk(self.path))
605 605 else:
606 606 walked_dir = [next(walk(self.path))]
607 607 walked_dir.sort()
608 608 for dirname, subdirs, fnames in walked_dir:
609 609 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
610 610 return '\n'.join(result_lines)
611 611
612 612
613 613 class Code(TextDisplayObject):
614 614 """Display syntax-highlighted source code.
615 615
616 616 This uses Pygments to highlight the code for HTML and Latex output.
617 617
618 618 Parameters
619 619 ----------
620 620 data : str
621 621 The code as a string
622 622 url : str
623 623 A URL to fetch the code from
624 624 filename : str
625 625 A local filename to load the code from
626 626 language : str
627 627 The short name of a Pygments lexer to use for highlighting.
628 628 If not specified, it will guess the lexer based on the filename
629 629 or the code. Available lexers: http://pygments.org/docs/lexers/
630 630 """
631 631 def __init__(self, data=None, url=None, filename=None, language=None):
632 632 self.language = language
633 633 super().__init__(data=data, url=url, filename=filename)
634 634
635 635 def _get_lexer(self):
636 636 if self.language:
637 637 from pygments.lexers import get_lexer_by_name
638 638 return get_lexer_by_name(self.language)
639 639 elif self.filename:
640 640 from pygments.lexers import get_lexer_for_filename
641 641 return get_lexer_for_filename(self.filename)
642 642 else:
643 643 from pygments.lexers import guess_lexer
644 644 return guess_lexer(self.data)
645 645
646 646 def __repr__(self):
647 647 return self.data
648 648
649 649 def _repr_html_(self):
650 650 from pygments import highlight
651 651 from pygments.formatters import HtmlFormatter
652 652 fmt = HtmlFormatter()
653 653 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
654 654 return style + highlight(self.data, self._get_lexer(), fmt)
655 655
656 656 def _repr_latex_(self):
657 657 from pygments import highlight
658 658 from pygments.formatters import LatexFormatter
659 659 return highlight(self.data, self._get_lexer(), LatexFormatter())
General Comments 0
You need to be logged in to leave comments. Login now