Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/IPython/core/display.py: 27%

463 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1# -*- coding: utf-8 -*- 

2"""Top-level display functions for displaying object in different formats.""" 

3 

4# Copyright (c) IPython Development Team. 

5# Distributed under the terms of the Modified BSD License. 

6 

7 

8from binascii import b2a_base64, hexlify 

9import html 

10import json 

11import mimetypes 

12import os 

13import struct 

14import warnings 

15from copy import deepcopy 

16from os.path import splitext 

17from pathlib import Path, PurePath 

18 

19from IPython.utils.py3compat import cast_unicode 

20from IPython.testing.skipdoctest import skip_doctest 

21from . import display_functions 

22 

23 

24__all__ = ['display_pretty', 'display_html', 'display_markdown', 

25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', 

26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', 

27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON', 

28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats', 

29 'set_matplotlib_close', 

30 'Video'] 

31 

32_deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"] 

33 

34__all__ = __all__ + _deprecated_names 

35 

36 

37# ----- warn to import from IPython.display ----- 

38 

39from warnings import warn 

40 

41 

42def __getattr__(name): 

43 if name in _deprecated_names: 

44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2) 

45 return getattr(display_functions, name) 

46 

47 if name in globals().keys(): 

48 return globals()[name] 

49 else: 

50 raise AttributeError(f"module {__name__} has no attribute {name}") 

51 

52 

53#----------------------------------------------------------------------------- 

54# utility functions 

55#----------------------------------------------------------------------------- 

56 

57def _safe_exists(path): 

58 """Check path, but don't let exceptions raise""" 

59 try: 

60 return os.path.exists(path) 

61 except Exception: 

62 return False 

63 

64 

65def _display_mimetype(mimetype, objs, raw=False, metadata=None): 

66 """internal implementation of all display_foo methods 

67 

68 Parameters 

69 ---------- 

70 mimetype : str 

71 The mimetype to be published (e.g. 'image/png') 

72 *objs : object 

73 The Python objects to display, or if raw=True raw text data to 

74 display. 

75 raw : bool 

76 Are the data objects raw data or Python objects that need to be 

77 formatted before display? [default: False] 

78 metadata : dict (optional) 

79 Metadata to be associated with the specific mimetype output. 

80 """ 

81 if metadata: 

82 metadata = {mimetype: metadata} 

83 if raw: 

84 # turn list of pngdata into list of { 'image/png': pngdata } 

85 objs = [ {mimetype: obj} for obj in objs ] 

86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype]) 

87 

88#----------------------------------------------------------------------------- 

89# Main functions 

90#----------------------------------------------------------------------------- 

91 

92 

93def display_pretty(*objs, **kwargs): 

94 """Display the pretty (default) representation of an object. 

95 

96 Parameters 

97 ---------- 

98 *objs : object 

99 The Python objects to display, or if raw=True raw text data to 

100 display. 

101 raw : bool 

102 Are the data objects raw data or Python objects that need to be 

103 formatted before display? [default: False] 

104 metadata : dict (optional) 

105 Metadata to be associated with the specific mimetype output. 

106 """ 

107 _display_mimetype('text/plain', objs, **kwargs) 

108 

109 

110def display_html(*objs, **kwargs): 

111 """Display the HTML representation of an object. 

112 

113 Note: If raw=False and the object does not have a HTML 

114 representation, no HTML will be shown. 

115 

116 Parameters 

117 ---------- 

118 *objs : object 

119 The Python objects to display, or if raw=True raw HTML data to 

120 display. 

121 raw : bool 

122 Are the data objects raw data or Python objects that need to be 

123 formatted before display? [default: False] 

124 metadata : dict (optional) 

125 Metadata to be associated with the specific mimetype output. 

126 """ 

127 _display_mimetype('text/html', objs, **kwargs) 

128 

129 

130def display_markdown(*objs, **kwargs): 

131 """Displays the Markdown representation of an object. 

132 

133 Parameters 

134 ---------- 

135 *objs : object 

136 The Python objects to display, or if raw=True raw markdown data to 

137 display. 

138 raw : bool 

139 Are the data objects raw data or Python objects that need to be 

140 formatted before display? [default: False] 

141 metadata : dict (optional) 

142 Metadata to be associated with the specific mimetype output. 

143 """ 

144 

145 _display_mimetype('text/markdown', objs, **kwargs) 

146 

147 

148def display_svg(*objs, **kwargs): 

149 """Display the SVG representation of an object. 

150 

151 Parameters 

152 ---------- 

153 *objs : object 

154 The Python objects to display, or if raw=True raw svg data to 

155 display. 

156 raw : bool 

157 Are the data objects raw data or Python objects that need to be 

158 formatted before display? [default: False] 

159 metadata : dict (optional) 

160 Metadata to be associated with the specific mimetype output. 

161 """ 

162 _display_mimetype('image/svg+xml', objs, **kwargs) 

163 

164 

165def display_png(*objs, **kwargs): 

166 """Display the PNG representation of an object. 

167 

168 Parameters 

169 ---------- 

170 *objs : object 

171 The Python objects to display, or if raw=True raw png data to 

172 display. 

173 raw : bool 

174 Are the data objects raw data or Python objects that need to be 

175 formatted before display? [default: False] 

176 metadata : dict (optional) 

177 Metadata to be associated with the specific mimetype output. 

178 """ 

179 _display_mimetype('image/png', objs, **kwargs) 

180 

181 

182def display_jpeg(*objs, **kwargs): 

183 """Display the JPEG representation of an object. 

184 

185 Parameters 

186 ---------- 

187 *objs : object 

188 The Python objects to display, or if raw=True raw JPEG data to 

189 display. 

190 raw : bool 

191 Are the data objects raw data or Python objects that need to be 

192 formatted before display? [default: False] 

193 metadata : dict (optional) 

194 Metadata to be associated with the specific mimetype output. 

195 """ 

196 _display_mimetype('image/jpeg', objs, **kwargs) 

197 

198 

199def display_latex(*objs, **kwargs): 

200 """Display the LaTeX representation of an object. 

201 

202 Parameters 

203 ---------- 

204 *objs : object 

205 The Python objects to display, or if raw=True raw latex data to 

206 display. 

207 raw : bool 

208 Are the data objects raw data or Python objects that need to be 

209 formatted before display? [default: False] 

210 metadata : dict (optional) 

211 Metadata to be associated with the specific mimetype output. 

212 """ 

213 _display_mimetype('text/latex', objs, **kwargs) 

214 

215 

216def display_json(*objs, **kwargs): 

217 """Display the JSON representation of an object. 

218 

219 Note that not many frontends support displaying JSON. 

220 

221 Parameters 

222 ---------- 

223 *objs : object 

224 The Python objects to display, or if raw=True raw json data to 

225 display. 

226 raw : bool 

227 Are the data objects raw data or Python objects that need to be 

228 formatted before display? [default: False] 

229 metadata : dict (optional) 

230 Metadata to be associated with the specific mimetype output. 

231 """ 

232 _display_mimetype('application/json', objs, **kwargs) 

233 

234 

235def display_javascript(*objs, **kwargs): 

236 """Display the Javascript representation of an object. 

237 

238 Parameters 

239 ---------- 

240 *objs : object 

241 The Python objects to display, or if raw=True raw javascript data to 

242 display. 

243 raw : bool 

244 Are the data objects raw data or Python objects that need to be 

245 formatted before display? [default: False] 

246 metadata : dict (optional) 

247 Metadata to be associated with the specific mimetype output. 

248 """ 

249 _display_mimetype('application/javascript', objs, **kwargs) 

250 

251 

252def display_pdf(*objs, **kwargs): 

253 """Display the PDF representation of an object. 

254 

255 Parameters 

256 ---------- 

257 *objs : object 

258 The Python objects to display, or if raw=True raw javascript data to 

259 display. 

260 raw : bool 

261 Are the data objects raw data or Python objects that need to be 

262 formatted before display? [default: False] 

263 metadata : dict (optional) 

264 Metadata to be associated with the specific mimetype output. 

265 """ 

266 _display_mimetype('application/pdf', objs, **kwargs) 

267 

268 

269#----------------------------------------------------------------------------- 

270# Smart classes 

271#----------------------------------------------------------------------------- 

272 

273 

274class DisplayObject(object): 

275 """An object that wraps data to be displayed.""" 

276 

277 _read_flags = 'r' 

278 _show_mem_addr = False 

279 metadata = None 

280 

281 def __init__(self, data=None, url=None, filename=None, metadata=None): 

282 """Create a display object given raw data. 

283 

284 When this object is returned by an expression or passed to the 

285 display function, it will result in the data being displayed 

286 in the frontend. The MIME type of the data should match the 

287 subclasses used, so the Png subclass should be used for 'image/png' 

288 data. If the data is a URL, the data will first be downloaded 

289 and then displayed. If 

290 

291 Parameters 

292 ---------- 

293 data : unicode, str or bytes 

294 The raw data or a URL or file to load the data from 

295 url : unicode 

296 A URL to download the data from. 

297 filename : unicode 

298 Path to a local file to load the data from. 

299 metadata : dict 

300 Dict of metadata associated to be the object when displayed 

301 """ 

302 if isinstance(data, (Path, PurePath)): 

303 data = str(data) 

304 

305 if data is not None and isinstance(data, str): 

306 if data.startswith('http') and url is None: 

307 url = data 

308 filename = None 

309 data = None 

310 elif _safe_exists(data) and filename is None: 

311 url = None 

312 filename = data 

313 data = None 

314 

315 self.url = url 

316 self.filename = filename 

317 # because of @data.setter methods in 

318 # subclasses ensure url and filename are set 

319 # before assigning to self.data 

320 self.data = data 

321 

322 if metadata is not None: 

323 self.metadata = metadata 

324 elif self.metadata is None: 

325 self.metadata = {} 

326 

327 self.reload() 

328 self._check_data() 

329 

330 def __repr__(self): 

331 if not self._show_mem_addr: 

332 cls = self.__class__ 

333 r = "<%s.%s object>" % (cls.__module__, cls.__name__) 

334 else: 

335 r = super(DisplayObject, self).__repr__() 

336 return r 

337 

338 def _check_data(self): 

339 """Override in subclasses if there's something to check.""" 

340 pass 

341 

342 def _data_and_metadata(self): 

343 """shortcut for returning metadata with shape information, if defined""" 

344 if self.metadata: 

345 return self.data, deepcopy(self.metadata) 

346 else: 

347 return self.data 

348 

349 def reload(self): 

350 """Reload the raw data from file or URL.""" 

351 if self.filename is not None: 

352 encoding = None if "b" in self._read_flags else "utf-8" 

353 with open(self.filename, self._read_flags, encoding=encoding) as f: 

354 self.data = f.read() 

355 elif self.url is not None: 

356 # Deferred import 

357 from urllib.request import urlopen 

358 response = urlopen(self.url) 

359 data = response.read() 

360 # extract encoding from header, if there is one: 

361 encoding = None 

362 if 'content-type' in response.headers: 

363 for sub in response.headers['content-type'].split(';'): 

364 sub = sub.strip() 

365 if sub.startswith('charset'): 

366 encoding = sub.split('=')[-1].strip() 

367 break 

368 if 'content-encoding' in response.headers: 

369 # TODO: do deflate? 

370 if 'gzip' in response.headers['content-encoding']: 

371 import gzip 

372 from io import BytesIO 

373 

374 # assume utf-8 if encoding is not specified 

375 with gzip.open( 

376 BytesIO(data), "rt", encoding=encoding or "utf-8" 

377 ) as fp: 

378 encoding = None 

379 data = fp.read() 

380 

381 # decode data, if an encoding was specified 

382 # We only touch self.data once since 

383 # subclasses such as SVG have @data.setter methods 

384 # that transform self.data into ... well svg. 

385 if encoding: 

386 self.data = data.decode(encoding, 'replace') 

387 else: 

388 self.data = data 

389 

390 

391class TextDisplayObject(DisplayObject): 

392 """Create a text display object given raw data. 

393 

394 Parameters 

395 ---------- 

396 data : str or unicode 

397 The raw data or a URL or file to load the data from. 

398 url : unicode 

399 A URL to download the data from. 

400 filename : unicode 

401 Path to a local file to load the data from. 

402 metadata : dict 

403 Dict of metadata associated to be the object when displayed 

404 """ 

405 def _check_data(self): 

406 if self.data is not None and not isinstance(self.data, str): 

407 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) 

408 

409class Pretty(TextDisplayObject): 

410 

411 def _repr_pretty_(self, pp, cycle): 

412 return pp.text(self.data) 

413 

414 

415class HTML(TextDisplayObject): 

416 

417 def __init__(self, data=None, url=None, filename=None, metadata=None): 

418 def warn(): 

419 if not data: 

420 return False 

421 

422 # 

423 # Avoid calling lower() on the entire data, because it could be a 

424 # long string and we're only interested in its beginning and end. 

425 # 

426 prefix = data[:10].lower() 

427 suffix = data[-10:].lower() 

428 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>") 

429 

430 if warn(): 

431 warnings.warn("Consider using IPython.display.IFrame instead") 

432 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata) 

433 

434 def _repr_html_(self): 

435 return self._data_and_metadata() 

436 

437 def __html__(self): 

438 """ 

439 This method exists to inform other HTML-using modules (e.g. Markupsafe, 

440 htmltag, etc) that this object is HTML and does not need things like 

441 special characters (<>&) escaped. 

442 """ 

443 return self._repr_html_() 

444 

445 

446class Markdown(TextDisplayObject): 

447 

448 def _repr_markdown_(self): 

449 return self._data_and_metadata() 

450 

451 

452class Math(TextDisplayObject): 

453 

454 def _repr_latex_(self): 

455 s = r"$\displaystyle %s$" % self.data.strip('$') 

456 if self.metadata: 

457 return s, deepcopy(self.metadata) 

458 else: 

459 return s 

460 

461 

462class Latex(TextDisplayObject): 

463 

464 def _repr_latex_(self): 

465 return self._data_and_metadata() 

466 

467 

468class SVG(DisplayObject): 

469 """Embed an SVG into the display. 

470 

471 Note if you just want to view a svg image via a URL use `:class:Image` with 

472 a url=URL keyword argument. 

473 """ 

474 

475 _read_flags = 'rb' 

476 # wrap data in a property, which extracts the <svg> tag, discarding 

477 # document headers 

478 _data = None 

479 

480 @property 

481 def data(self): 

482 return self._data 

483 

484 @data.setter 

485 def data(self, svg): 

486 if svg is None: 

487 self._data = None 

488 return 

489 # parse into dom object 

490 from xml.dom import minidom 

491 x = minidom.parseString(svg) 

492 # get svg tag (should be 1) 

493 found_svg = x.getElementsByTagName('svg') 

494 if found_svg: 

495 svg = found_svg[0].toxml() 

496 else: 

497 # fallback on the input, trust the user 

498 # but this is probably an error. 

499 pass 

500 svg = cast_unicode(svg) 

501 self._data = svg 

502 

503 def _repr_svg_(self): 

504 return self._data_and_metadata() 

505 

506class ProgressBar(DisplayObject): 

507 """Progressbar supports displaying a progressbar like element 

508 """ 

509 def __init__(self, total): 

510 """Creates a new progressbar 

511 

512 Parameters 

513 ---------- 

514 total : int 

515 maximum size of the progressbar 

516 """ 

517 self.total = total 

518 self._progress = 0 

519 self.html_width = '60ex' 

520 self.text_width = 60 

521 self._display_id = hexlify(os.urandom(8)).decode('ascii') 

522 

523 def __repr__(self): 

524 fraction = self.progress / self.total 

525 filled = '=' * int(fraction * self.text_width) 

526 rest = ' ' * (self.text_width - len(filled)) 

527 return '[{}{}] {}/{}'.format( 

528 filled, rest, 

529 self.progress, self.total, 

530 ) 

531 

532 def _repr_html_(self): 

533 return "<progress style='width:{}' max='{}' value='{}'></progress>".format( 

534 self.html_width, self.total, self.progress) 

535 

536 def display(self): 

537 display_functions.display(self, display_id=self._display_id) 

538 

539 def update(self): 

540 display_functions.display(self, display_id=self._display_id, update=True) 

541 

542 @property 

543 def progress(self): 

544 return self._progress 

545 

546 @progress.setter 

547 def progress(self, value): 

548 self._progress = value 

549 self.update() 

550 

551 def __iter__(self): 

552 self.display() 

553 self._progress = -1 # First iteration is 0 

554 return self 

555 

556 def __next__(self): 

557 """Returns current value and increments display by one.""" 

558 self.progress += 1 

559 if self.progress < self.total: 

560 return self.progress 

561 else: 

562 raise StopIteration() 

563 

564class JSON(DisplayObject): 

565 """JSON expects a JSON-able dict or list 

566 

567 not an already-serialized JSON string. 

568 

569 Scalar types (None, number, string) are not allowed, only dict or list containers. 

570 """ 

571 # wrap data in a property, which warns about passing already-serialized JSON 

572 _data = None 

573 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs): 

574 """Create a JSON display object given raw data. 

575 

576 Parameters 

577 ---------- 

578 data : dict or list 

579 JSON data to display. Not an already-serialized JSON string. 

580 Scalar types (None, number, string) are not allowed, only dict 

581 or list containers. 

582 url : unicode 

583 A URL to download the data from. 

584 filename : unicode 

585 Path to a local file to load the data from. 

586 expanded : boolean 

587 Metadata to control whether a JSON display component is expanded. 

588 metadata : dict 

589 Specify extra metadata to attach to the json display object. 

590 root : str 

591 The name of the root element of the JSON tree 

592 """ 

593 self.metadata = { 

594 'expanded': expanded, 

595 'root': root, 

596 } 

597 if metadata: 

598 self.metadata.update(metadata) 

599 if kwargs: 

600 self.metadata.update(kwargs) 

601 super(JSON, self).__init__(data=data, url=url, filename=filename) 

602 

603 def _check_data(self): 

604 if self.data is not None and not isinstance(self.data, (dict, list)): 

605 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data)) 

606 

607 @property 

608 def data(self): 

609 return self._data 

610 

611 @data.setter 

612 def data(self, data): 

613 if isinstance(data, (Path, PurePath)): 

614 data = str(data) 

615 

616 if isinstance(data, str): 

617 if self.filename is None and self.url is None: 

618 warnings.warn("JSON expects JSONable dict or list, not JSON strings") 

619 data = json.loads(data) 

620 self._data = data 

621 

622 def _data_and_metadata(self): 

623 return self.data, self.metadata 

624 

625 def _repr_json_(self): 

626 return self._data_and_metadata() 

627 

628 

629_css_t = """var link = document.createElement("link"); 

630 link.rel = "stylesheet"; 

631 link.type = "text/css"; 

632 link.href = "%s"; 

633 document.head.appendChild(link); 

634""" 

635 

636_lib_t1 = """new Promise(function(resolve, reject) { 

637 var script = document.createElement("script"); 

638 script.onload = resolve; 

639 script.onerror = reject; 

640 script.src = "%s"; 

641 document.head.appendChild(script); 

642}).then(() => { 

643""" 

644 

645_lib_t2 = """ 

646});""" 

647 

648class GeoJSON(JSON): 

649 """GeoJSON expects JSON-able dict 

650 

651 not an already-serialized JSON string. 

652 

653 Scalar types (None, number, string) are not allowed, only dict containers. 

654 """ 

655 

656 def __init__(self, *args, **kwargs): 

657 """Create a GeoJSON display object given raw data. 

658 

659 Parameters 

660 ---------- 

661 data : dict or list 

662 VegaLite data. Not an already-serialized JSON string. 

663 Scalar types (None, number, string) are not allowed, only dict 

664 or list containers. 

665 url_template : string 

666 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template 

667 layer_options : dict 

668 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options 

669 url : unicode 

670 A URL to download the data from. 

671 filename : unicode 

672 Path to a local file to load the data from. 

673 metadata : dict 

674 Specify extra metadata to attach to the json display object. 

675 

676 Examples 

677 -------- 

678 The following will display an interactive map of Mars with a point of 

679 interest on frontend that do support GeoJSON display. 

680 

681 >>> from IPython.display import GeoJSON 

682 

683 >>> GeoJSON(data={ 

684 ... "type": "Feature", 

685 ... "geometry": { 

686 ... "type": "Point", 

687 ... "coordinates": [-81.327, 296.038] 

688 ... } 

689 ... }, 

690 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png", 

691 ... layer_options={ 

692 ... "basemap_id": "celestia_mars-shaded-16k_global", 

693 ... "attribution" : "Celestia/praesepe", 

694 ... "minZoom" : 0, 

695 ... "maxZoom" : 18, 

696 ... }) 

697 <IPython.core.display.GeoJSON object> 

698 

699 In the terminal IPython, you will only see the text representation of 

700 the GeoJSON object. 

701 

702 """ 

703 

704 super(GeoJSON, self).__init__(*args, **kwargs) 

705 

706 

707 def _ipython_display_(self): 

708 bundle = { 

709 'application/geo+json': self.data, 

710 'text/plain': '<IPython.display.GeoJSON object>' 

711 } 

712 metadata = { 

713 'application/geo+json': self.metadata 

714 } 

715 display_functions.display(bundle, metadata=metadata, raw=True) 

716 

717class Javascript(TextDisplayObject): 

718 

719 def __init__(self, data=None, url=None, filename=None, lib=None, css=None): 

720 """Create a Javascript display object given raw data. 

721 

722 When this object is returned by an expression or passed to the 

723 display function, it will result in the data being displayed 

724 in the frontend. If the data is a URL, the data will first be 

725 downloaded and then displayed. 

726 

727 In the Notebook, the containing element will be available as `element`, 

728 and jQuery will be available. Content appended to `element` will be 

729 visible in the output area. 

730 

731 Parameters 

732 ---------- 

733 data : unicode, str or bytes 

734 The Javascript source code or a URL to download it from. 

735 url : unicode 

736 A URL to download the data from. 

737 filename : unicode 

738 Path to a local file to load the data from. 

739 lib : list or str 

740 A sequence of Javascript library URLs to load asynchronously before 

741 running the source code. The full URLs of the libraries should 

742 be given. A single Javascript library URL can also be given as a 

743 string. 

744 css : list or str 

745 A sequence of css files to load before running the source code. 

746 The full URLs of the css files should be given. A single css URL 

747 can also be given as a string. 

748 """ 

749 if isinstance(lib, str): 

750 lib = [lib] 

751 elif lib is None: 

752 lib = [] 

753 if isinstance(css, str): 

754 css = [css] 

755 elif css is None: 

756 css = [] 

757 if not isinstance(lib, (list,tuple)): 

758 raise TypeError('expected sequence, got: %r' % lib) 

759 if not isinstance(css, (list,tuple)): 

760 raise TypeError('expected sequence, got: %r' % css) 

761 self.lib = lib 

762 self.css = css 

763 super(Javascript, self).__init__(data=data, url=url, filename=filename) 

764 

765 def _repr_javascript_(self): 

766 r = '' 

767 for c in self.css: 

768 r += _css_t % c 

769 for l in self.lib: 

770 r += _lib_t1 % l 

771 r += self.data 

772 r += _lib_t2*len(self.lib) 

773 return r 

774 

775# constants for identifying png/jpeg data 

776_PNG = b'\x89PNG\r\n\x1a\n' 

777_JPEG = b'\xff\xd8' 

778 

779def _pngxy(data): 

780 """read the (width, height) from a PNG header""" 

781 ihdr = data.index(b'IHDR') 

782 # next 8 bytes are width/height 

783 return struct.unpack('>ii', data[ihdr+4:ihdr+12]) 

784 

785def _jpegxy(data): 

786 """read the (width, height) from a JPEG header""" 

787 # adapted from http://www.64lines.com/jpeg-width-height 

788 

789 idx = 4 

790 while True: 

791 block_size = struct.unpack('>H', data[idx:idx+2])[0] 

792 idx = idx + block_size 

793 if data[idx:idx+2] == b'\xFF\xC0': 

794 # found Start of Frame 

795 iSOF = idx 

796 break 

797 else: 

798 # read another block 

799 idx += 2 

800 

801 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9]) 

802 return w, h 

803 

804def _gifxy(data): 

805 """read the (width, height) from a GIF header""" 

806 return struct.unpack('<HH', data[6:10]) 

807 

808 

809class Image(DisplayObject): 

810 

811 _read_flags = 'rb' 

812 _FMT_JPEG = u'jpeg' 

813 _FMT_PNG = u'png' 

814 _FMT_GIF = u'gif' 

815 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF] 

816 _MIMETYPES = { 

817 _FMT_PNG: 'image/png', 

818 _FMT_JPEG: 'image/jpeg', 

819 _FMT_GIF: 'image/gif', 

820 } 

821 

822 def __init__( 

823 self, 

824 data=None, 

825 url=None, 

826 filename=None, 

827 format=None, 

828 embed=None, 

829 width=None, 

830 height=None, 

831 retina=False, 

832 unconfined=False, 

833 metadata=None, 

834 alt=None, 

835 ): 

836 """Create a PNG/JPEG/GIF image object given raw data. 

837 

838 When this object is returned by an input cell or passed to the 

839 display function, it will result in the image being displayed 

840 in the frontend. 

841 

842 Parameters 

843 ---------- 

844 data : unicode, str or bytes 

845 The raw image data or a URL or filename to load the data from. 

846 This always results in embedded image data. 

847 

848 url : unicode 

849 A URL to download the data from. If you specify `url=`, 

850 the image data will not be embedded unless you also specify `embed=True`. 

851 

852 filename : unicode 

853 Path to a local file to load the data from. 

854 Images from a file are always embedded. 

855 

856 format : unicode 

857 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given 

858 for format will be inferred from the filename extension. 

859 

860 embed : bool 

861 Should the image data be embedded using a data URI (True) or be 

862 loaded using an <img> tag. Set this to True if you want the image 

863 to be viewable later with no internet connection in the notebook. 

864 

865 Default is `True`, unless the keyword argument `url` is set, then 

866 default value is `False`. 

867 

868 Note that QtConsole is not able to display images if `embed` is set to `False` 

869 

870 width : int 

871 Width in pixels to which to constrain the image in html 

872 

873 height : int 

874 Height in pixels to which to constrain the image in html 

875 

876 retina : bool 

877 Automatically set the width and height to half of the measured 

878 width and height. 

879 This only works for embedded images because it reads the width/height 

880 from image data. 

881 For non-embedded images, you can just set the desired display width 

882 and height directly. 

883 

884 unconfined : bool 

885 Set unconfined=True to disable max-width confinement of the image. 

886 

887 metadata : dict 

888 Specify extra metadata to attach to the image. 

889 

890 alt : unicode 

891 Alternative text for the image, for use by screen readers. 

892 

893 Examples 

894 -------- 

895 embedded image data, works in qtconsole and notebook 

896 when passed positionally, the first arg can be any of raw image data, 

897 a URL, or a filename from which to load image data. 

898 The result is always embedding image data for inline images. 

899 

900 >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP 

901 <IPython.core.display.Image object> 

902 

903 >>> Image('/path/to/image.jpg') 

904 <IPython.core.display.Image object> 

905 

906 >>> Image(b'RAW_PNG_DATA...') 

907 <IPython.core.display.Image object> 

908 

909 Specifying Image(url=...) does not embed the image data, 

910 it only generates ``<img>`` tag with a link to the source. 

911 This will not work in the qtconsole or offline. 

912 

913 >>> Image(url='https://www.google.fr/images/srpr/logo3w.png') 

914 <IPython.core.display.Image object> 

915 

916 """ 

917 if isinstance(data, (Path, PurePath)): 

918 data = str(data) 

919 

920 if filename is not None: 

921 ext = self._find_ext(filename) 

922 elif url is not None: 

923 ext = self._find_ext(url) 

924 elif data is None: 

925 raise ValueError("No image data found. Expecting filename, url, or data.") 

926 elif isinstance(data, str) and ( 

927 data.startswith('http') or _safe_exists(data) 

928 ): 

929 ext = self._find_ext(data) 

930 else: 

931 ext = None 

932 

933 if format is None: 

934 if ext is not None: 

935 if ext == u'jpg' or ext == u'jpeg': 

936 format = self._FMT_JPEG 

937 elif ext == u'png': 

938 format = self._FMT_PNG 

939 elif ext == u'gif': 

940 format = self._FMT_GIF 

941 else: 

942 format = ext.lower() 

943 elif isinstance(data, bytes): 

944 # infer image type from image data header, 

945 # only if format has not been specified. 

946 if data[:2] == _JPEG: 

947 format = self._FMT_JPEG 

948 

949 # failed to detect format, default png 

950 if format is None: 

951 format = self._FMT_PNG 

952 

953 if format.lower() == 'jpg': 

954 # jpg->jpeg 

955 format = self._FMT_JPEG 

956 

957 self.format = format.lower() 

958 self.embed = embed if embed is not None else (url is None) 

959 

960 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS: 

961 raise ValueError("Cannot embed the '%s' image format" % (self.format)) 

962 if self.embed: 

963 self._mimetype = self._MIMETYPES.get(self.format) 

964 

965 self.width = width 

966 self.height = height 

967 self.retina = retina 

968 self.unconfined = unconfined 

969 self.alt = alt 

970 super(Image, self).__init__(data=data, url=url, filename=filename, 

971 metadata=metadata) 

972 

973 if self.width is None and self.metadata.get('width', {}): 

974 self.width = metadata['width'] 

975 

976 if self.height is None and self.metadata.get('height', {}): 

977 self.height = metadata['height'] 

978 

979 if self.alt is None and self.metadata.get("alt", {}): 

980 self.alt = metadata["alt"] 

981 

982 if retina: 

983 self._retina_shape() 

984 

985 

986 def _retina_shape(self): 

987 """load pixel-doubled width and height from image data""" 

988 if not self.embed: 

989 return 

990 if self.format == self._FMT_PNG: 

991 w, h = _pngxy(self.data) 

992 elif self.format == self._FMT_JPEG: 

993 w, h = _jpegxy(self.data) 

994 elif self.format == self._FMT_GIF: 

995 w, h = _gifxy(self.data) 

996 else: 

997 # retina only supports png 

998 return 

999 self.width = w // 2 

1000 self.height = h // 2 

1001 

1002 def reload(self): 

1003 """Reload the raw data from file or URL.""" 

1004 if self.embed: 

1005 super(Image,self).reload() 

1006 if self.retina: 

1007 self._retina_shape() 

1008 

1009 def _repr_html_(self): 

1010 if not self.embed: 

1011 width = height = klass = alt = "" 

1012 if self.width: 

1013 width = ' width="%d"' % self.width 

1014 if self.height: 

1015 height = ' height="%d"' % self.height 

1016 if self.unconfined: 

1017 klass = ' class="unconfined"' 

1018 if self.alt: 

1019 alt = ' alt="%s"' % html.escape(self.alt) 

1020 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format( 

1021 url=self.url, 

1022 width=width, 

1023 height=height, 

1024 klass=klass, 

1025 alt=alt, 

1026 ) 

1027 

1028 def _repr_mimebundle_(self, include=None, exclude=None): 

1029 """Return the image as a mimebundle 

1030 

1031 Any new mimetype support should be implemented here. 

1032 """ 

1033 if self.embed: 

1034 mimetype = self._mimetype 

1035 data, metadata = self._data_and_metadata(always_both=True) 

1036 if metadata: 

1037 metadata = {mimetype: metadata} 

1038 return {mimetype: data}, metadata 

1039 else: 

1040 return {'text/html': self._repr_html_()} 

1041 

1042 def _data_and_metadata(self, always_both=False): 

1043 """shortcut for returning metadata with shape information, if defined""" 

1044 try: 

1045 b64_data = b2a_base64(self.data, newline=False).decode("ascii") 

1046 except TypeError as e: 

1047 raise FileNotFoundError( 

1048 "No such file or directory: '%s'" % (self.data)) from e 

1049 md = {} 

1050 if self.metadata: 

1051 md.update(self.metadata) 

1052 if self.width: 

1053 md['width'] = self.width 

1054 if self.height: 

1055 md['height'] = self.height 

1056 if self.unconfined: 

1057 md['unconfined'] = self.unconfined 

1058 if self.alt: 

1059 md["alt"] = self.alt 

1060 if md or always_both: 

1061 return b64_data, md 

1062 else: 

1063 return b64_data 

1064 

1065 def _repr_png_(self): 

1066 if self.embed and self.format == self._FMT_PNG: 

1067 return self._data_and_metadata() 

1068 

1069 def _repr_jpeg_(self): 

1070 if self.embed and self.format == self._FMT_JPEG: 

1071 return self._data_and_metadata() 

1072 

1073 def _find_ext(self, s): 

1074 base, ext = splitext(s) 

1075 

1076 if not ext: 

1077 return base 

1078 

1079 # `splitext` includes leading period, so we skip it 

1080 return ext[1:].lower() 

1081 

1082 

1083class Video(DisplayObject): 

1084 

1085 def __init__(self, data=None, url=None, filename=None, embed=False, 

1086 mimetype=None, width=None, height=None, html_attributes="controls"): 

1087 """Create a video object given raw data or an URL. 

1088 

1089 When this object is returned by an input cell or passed to the 

1090 display function, it will result in the video being displayed 

1091 in the frontend. 

1092 

1093 Parameters 

1094 ---------- 

1095 data : unicode, str or bytes 

1096 The raw video data or a URL or filename to load the data from. 

1097 Raw data will require passing ``embed=True``. 

1098 

1099 url : unicode 

1100 A URL for the video. If you specify ``url=``, 

1101 the image data will not be embedded. 

1102 

1103 filename : unicode 

1104 Path to a local file containing the video. 

1105 Will be interpreted as a local URL unless ``embed=True``. 

1106 

1107 embed : bool 

1108 Should the video be embedded using a data URI (True) or be 

1109 loaded using a <video> tag (False). 

1110 

1111 Since videos are large, embedding them should be avoided, if possible. 

1112 You must confirm embedding as your intention by passing ``embed=True``. 

1113 

1114 Local files can be displayed with URLs without embedding the content, via:: 

1115 

1116 Video('./video.mp4') 

1117 

1118 mimetype : unicode 

1119 Specify the mimetype for embedded videos. 

1120 Default will be guessed from file extension, if available. 

1121 

1122 width : int 

1123 Width in pixels to which to constrain the video in HTML. 

1124 If not supplied, defaults to the width of the video. 

1125 

1126 height : int 

1127 Height in pixels to which to constrain the video in html. 

1128 If not supplied, defaults to the height of the video. 

1129 

1130 html_attributes : str 

1131 Attributes for the HTML ``<video>`` block. 

1132 Default: ``"controls"`` to get video controls. 

1133 Other examples: ``"controls muted"`` for muted video with controls, 

1134 ``"loop autoplay"`` for looping autoplaying video without controls. 

1135 

1136 Examples 

1137 -------- 

1138 :: 

1139 

1140 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4') 

1141 Video('path/to/video.mp4') 

1142 Video('path/to/video.mp4', embed=True) 

1143 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay") 

1144 Video(b'raw-videodata', embed=True) 

1145 """ 

1146 if isinstance(data, (Path, PurePath)): 

1147 data = str(data) 

1148 

1149 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')): 

1150 url = data 

1151 data = None 

1152 elif data is not None and os.path.exists(data): 

1153 filename = data 

1154 data = None 

1155 

1156 if data and not embed: 

1157 msg = ''.join([ 

1158 "To embed videos, you must pass embed=True ", 

1159 "(this may make your notebook files huge)\n", 

1160 "Consider passing Video(url='...')", 

1161 ]) 

1162 raise ValueError(msg) 

1163 

1164 self.mimetype = mimetype 

1165 self.embed = embed 

1166 self.width = width 

1167 self.height = height 

1168 self.html_attributes = html_attributes 

1169 super(Video, self).__init__(data=data, url=url, filename=filename) 

1170 

1171 def _repr_html_(self): 

1172 width = height = '' 

1173 if self.width: 

1174 width = ' width="%d"' % self.width 

1175 if self.height: 

1176 height = ' height="%d"' % self.height 

1177 

1178 # External URLs and potentially local files are not embedded into the 

1179 # notebook output. 

1180 if not self.embed: 

1181 url = self.url if self.url is not None else self.filename 

1182 output = """<video src="{0}" {1} {2} {3}> 

1183 Your browser does not support the <code>video</code> element. 

1184 </video>""".format(url, self.html_attributes, width, height) 

1185 return output 

1186 

1187 # Embedded videos are base64-encoded. 

1188 mimetype = self.mimetype 

1189 if self.filename is not None: 

1190 if not mimetype: 

1191 mimetype, _ = mimetypes.guess_type(self.filename) 

1192 

1193 with open(self.filename, 'rb') as f: 

1194 video = f.read() 

1195 else: 

1196 video = self.data 

1197 if isinstance(video, str): 

1198 # unicode input is already b64-encoded 

1199 b64_video = video 

1200 else: 

1201 b64_video = b2a_base64(video, newline=False).decode("ascii").rstrip() 

1202 

1203 output = """<video {0} {1} {2}> 

1204 <source src="data:{3};base64,{4}" type="{3}"> 

1205 Your browser does not support the video tag. 

1206 </video>""".format(self.html_attributes, width, height, mimetype, b64_video) 

1207 return output 

1208 

1209 def reload(self): 

1210 # TODO 

1211 pass 

1212 

1213 

1214@skip_doctest 

1215def set_matplotlib_formats(*formats, **kwargs): 

1216 """ 

1217 .. deprecated:: 7.23 

1218 

1219 use `matplotlib_inline.backend_inline.set_matplotlib_formats()` 

1220 

1221 Select figure formats for the inline backend. Optionally pass quality for JPEG. 

1222 

1223 For example, this enables PNG and JPEG output with a JPEG quality of 90%:: 

1224 

1225 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90) 

1226 

1227 To set this in your config files use the following:: 

1228 

1229 c.InlineBackend.figure_formats = {'png', 'jpeg'} 

1230 c.InlineBackend.print_figure_kwargs.update({'quality' : 90}) 

1231 

1232 Parameters 

1233 ---------- 

1234 *formats : strs 

1235 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'. 

1236 **kwargs 

1237 Keyword args will be relayed to ``figure.canvas.print_figure``. 

1238 """ 

1239 warnings.warn( 

1240 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly " 

1241 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`", 

1242 DeprecationWarning, 

1243 stacklevel=2, 

1244 ) 

1245 

1246 from matplotlib_inline.backend_inline import ( 

1247 set_matplotlib_formats as set_matplotlib_formats_orig, 

1248 ) 

1249 

1250 set_matplotlib_formats_orig(*formats, **kwargs) 

1251 

1252@skip_doctest 

1253def set_matplotlib_close(close=True): 

1254 """ 

1255 .. deprecated:: 7.23 

1256 

1257 use `matplotlib_inline.backend_inline.set_matplotlib_close()` 

1258 

1259 Set whether the inline backend closes all figures automatically or not. 

1260 

1261 By default, the inline backend used in the IPython Notebook will close all 

1262 matplotlib figures automatically after each cell is run. This means that 

1263 plots in different cells won't interfere. Sometimes, you may want to make 

1264 a plot in one cell and then refine it in later cells. This can be accomplished 

1265 by:: 

1266 

1267 In [1]: set_matplotlib_close(False) 

1268 

1269 To set this in your config files use the following:: 

1270 

1271 c.InlineBackend.close_figures = False 

1272 

1273 Parameters 

1274 ---------- 

1275 close : bool 

1276 Should all matplotlib figures be automatically closed after each cell is 

1277 run? 

1278 """ 

1279 warnings.warn( 

1280 "`set_matplotlib_close` is deprecated since IPython 7.23, directly " 

1281 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`", 

1282 DeprecationWarning, 

1283 stacklevel=2, 

1284 ) 

1285 

1286 from matplotlib_inline.backend_inline import ( 

1287 set_matplotlib_close as set_matplotlib_close_orig, 

1288 ) 

1289 

1290 set_matplotlib_close_orig(close)