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