##// END OF EJS Templates
Try to avoid network connection during tests....
Matthias Bussonnier -
Show More
@@ -1,1277 +1,1277 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 html
10 10 import json
11 11 import mimetypes
12 12 import os
13 13 import struct
14 14 import warnings
15 15 from copy import deepcopy
16 16 from os.path import splitext
17 17 from pathlib import Path, PurePath
18 18
19 19 from IPython.utils.py3compat import cast_unicode
20 20 from IPython.testing.skipdoctest import skip_doctest
21 21 from . import display_functions
22 22
23 23
24 24 __all__ = ['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', 'ProgressBar', 'JSON',
28 28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 29 'set_matplotlib_close',
30 30 'Video']
31 31
32 32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33 33
34 34 __all__ = __all__ + _deprecated_names
35 35
36 36
37 37 # ----- warn to import from IPython.display -----
38 38
39 39 from warnings import warn
40 40
41 41
42 42 def __getattr__(name):
43 43 if name in _deprecated_names:
44 44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 45 return getattr(display_functions, name)
46 46
47 47 if name in globals().keys():
48 48 return globals()[name]
49 49 else:
50 50 raise AttributeError(f"module {__name__} has no attribute {name}")
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # utility functions
55 55 #-----------------------------------------------------------------------------
56 56
57 57 def _safe_exists(path):
58 58 """Check path, but don't let exceptions raise"""
59 59 try:
60 60 return os.path.exists(path)
61 61 except Exception:
62 62 return False
63 63
64 64
65 65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 66 """internal implementation of all display_foo methods
67 67
68 68 Parameters
69 69 ----------
70 70 mimetype : str
71 71 The mimetype to be published (e.g. 'image/png')
72 72 *objs : object
73 73 The Python objects to display, or if raw=True raw text data to
74 74 display.
75 75 raw : bool
76 76 Are the data objects raw data or Python objects that need to be
77 77 formatted before display? [default: False]
78 78 metadata : dict (optional)
79 79 Metadata to be associated with the specific mimetype output.
80 80 """
81 81 if metadata:
82 82 metadata = {mimetype: metadata}
83 83 if raw:
84 84 # turn list of pngdata into list of { 'image/png': pngdata }
85 85 objs = [ {mimetype: obj} for obj in objs ]
86 86 display_functions.display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Main functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92
93 93 def display_pretty(*objs, **kwargs):
94 94 """Display the pretty (default) representation of an object.
95 95
96 96 Parameters
97 97 ----------
98 98 *objs : object
99 99 The Python objects to display, or if raw=True raw text data to
100 100 display.
101 101 raw : bool
102 102 Are the data objects raw data or Python objects that need to be
103 103 formatted before display? [default: False]
104 104 metadata : dict (optional)
105 105 Metadata to be associated with the specific mimetype output.
106 106 """
107 107 _display_mimetype('text/plain', objs, **kwargs)
108 108
109 109
110 110 def display_html(*objs, **kwargs):
111 111 """Display the HTML representation of an object.
112 112
113 113 Note: If raw=False and the object does not have a HTML
114 114 representation, no HTML will be shown.
115 115
116 116 Parameters
117 117 ----------
118 118 *objs : object
119 119 The Python objects to display, or if raw=True raw HTML data to
120 120 display.
121 121 raw : bool
122 122 Are the data objects raw data or Python objects that need to be
123 123 formatted before display? [default: False]
124 124 metadata : dict (optional)
125 125 Metadata to be associated with the specific mimetype output.
126 126 """
127 127 _display_mimetype('text/html', objs, **kwargs)
128 128
129 129
130 130 def display_markdown(*objs, **kwargs):
131 131 """Displays the Markdown representation of an object.
132 132
133 133 Parameters
134 134 ----------
135 135 *objs : object
136 136 The Python objects to display, or if raw=True raw markdown data to
137 137 display.
138 138 raw : bool
139 139 Are the data objects raw data or Python objects that need to be
140 140 formatted before display? [default: False]
141 141 metadata : dict (optional)
142 142 Metadata to be associated with the specific mimetype output.
143 143 """
144 144
145 145 _display_mimetype('text/markdown', objs, **kwargs)
146 146
147 147
148 148 def display_svg(*objs, **kwargs):
149 149 """Display the SVG representation of an object.
150 150
151 151 Parameters
152 152 ----------
153 153 *objs : object
154 154 The Python objects to display, or if raw=True raw svg data to
155 155 display.
156 156 raw : bool
157 157 Are the data objects raw data or Python objects that need to be
158 158 formatted before display? [default: False]
159 159 metadata : dict (optional)
160 160 Metadata to be associated with the specific mimetype output.
161 161 """
162 162 _display_mimetype('image/svg+xml', objs, **kwargs)
163 163
164 164
165 165 def display_png(*objs, **kwargs):
166 166 """Display the PNG representation of an object.
167 167
168 168 Parameters
169 169 ----------
170 170 *objs : object
171 171 The Python objects to display, or if raw=True raw png data to
172 172 display.
173 173 raw : bool
174 174 Are the data objects raw data or Python objects that need to be
175 175 formatted before display? [default: False]
176 176 metadata : dict (optional)
177 177 Metadata to be associated with the specific mimetype output.
178 178 """
179 179 _display_mimetype('image/png', objs, **kwargs)
180 180
181 181
182 182 def display_jpeg(*objs, **kwargs):
183 183 """Display the JPEG representation of an object.
184 184
185 185 Parameters
186 186 ----------
187 187 *objs : object
188 188 The Python objects to display, or if raw=True raw JPEG data to
189 189 display.
190 190 raw : bool
191 191 Are the data objects raw data or Python objects that need to be
192 192 formatted before display? [default: False]
193 193 metadata : dict (optional)
194 194 Metadata to be associated with the specific mimetype output.
195 195 """
196 196 _display_mimetype('image/jpeg', objs, **kwargs)
197 197
198 198
199 199 def display_latex(*objs, **kwargs):
200 200 """Display the LaTeX representation of an object.
201 201
202 202 Parameters
203 203 ----------
204 204 *objs : object
205 205 The Python objects to display, or if raw=True raw latex data to
206 206 display.
207 207 raw : bool
208 208 Are the data objects raw data or Python objects that need to be
209 209 formatted before display? [default: False]
210 210 metadata : dict (optional)
211 211 Metadata to be associated with the specific mimetype output.
212 212 """
213 213 _display_mimetype('text/latex', objs, **kwargs)
214 214
215 215
216 216 def display_json(*objs, **kwargs):
217 217 """Display the JSON representation of an object.
218 218
219 219 Note that not many frontends support displaying JSON.
220 220
221 221 Parameters
222 222 ----------
223 223 *objs : object
224 224 The Python objects to display, or if raw=True raw json data to
225 225 display.
226 226 raw : bool
227 227 Are the data objects raw data or Python objects that need to be
228 228 formatted before display? [default: False]
229 229 metadata : dict (optional)
230 230 Metadata to be associated with the specific mimetype output.
231 231 """
232 232 _display_mimetype('application/json', objs, **kwargs)
233 233
234 234
235 235 def display_javascript(*objs, **kwargs):
236 236 """Display the Javascript representation of an object.
237 237
238 238 Parameters
239 239 ----------
240 240 *objs : object
241 241 The Python objects to display, or if raw=True raw javascript data to
242 242 display.
243 243 raw : bool
244 244 Are the data objects raw data or Python objects that need to be
245 245 formatted before display? [default: False]
246 246 metadata : dict (optional)
247 247 Metadata to be associated with the specific mimetype output.
248 248 """
249 249 _display_mimetype('application/javascript', objs, **kwargs)
250 250
251 251
252 252 def display_pdf(*objs, **kwargs):
253 253 """Display the PDF representation of an object.
254 254
255 255 Parameters
256 256 ----------
257 257 *objs : object
258 258 The Python objects to display, or if raw=True raw javascript data to
259 259 display.
260 260 raw : bool
261 261 Are the data objects raw data or Python objects that need to be
262 262 formatted before display? [default: False]
263 263 metadata : dict (optional)
264 264 Metadata to be associated with the specific mimetype output.
265 265 """
266 266 _display_mimetype('application/pdf', objs, **kwargs)
267 267
268 268
269 269 #-----------------------------------------------------------------------------
270 270 # Smart classes
271 271 #-----------------------------------------------------------------------------
272 272
273 273
274 274 class DisplayObject(object):
275 275 """An object that wraps data to be displayed."""
276 276
277 277 _read_flags = 'r'
278 278 _show_mem_addr = False
279 279 metadata = None
280 280
281 281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 282 """Create a display object given raw data.
283 283
284 284 When this object is returned by an expression or passed to the
285 285 display function, it will result in the data being displayed
286 286 in the frontend. The MIME type of the data should match the
287 287 subclasses used, so the Png subclass should be used for 'image/png'
288 288 data. If the data is a URL, the data will first be downloaded
289 289 and then displayed. If
290 290
291 291 Parameters
292 292 ----------
293 293 data : unicode, str or bytes
294 294 The raw data or a URL or file to load the data from
295 295 url : unicode
296 296 A URL to download the data from.
297 297 filename : unicode
298 298 Path to a local file to load the data from.
299 299 metadata : dict
300 300 Dict of metadata associated to be the object when displayed
301 301 """
302 302 if isinstance(data, (Path, PurePath)):
303 303 data = str(data)
304 304
305 305 if data is not None and isinstance(data, str):
306 306 if data.startswith('http') and url is None:
307 307 url = data
308 308 filename = None
309 309 data = None
310 310 elif _safe_exists(data) and filename is None:
311 311 url = None
312 312 filename = data
313 313 data = None
314 314
315 315 self.url = url
316 316 self.filename = filename
317 317 # because of @data.setter methods in
318 318 # subclasses ensure url and filename are set
319 319 # before assigning to self.data
320 320 self.data = data
321 321
322 322 if metadata is not None:
323 323 self.metadata = metadata
324 324 elif self.metadata is None:
325 325 self.metadata = {}
326 326
327 327 self.reload()
328 328 self._check_data()
329 329
330 330 def __repr__(self):
331 331 if not self._show_mem_addr:
332 332 cls = self.__class__
333 333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 334 else:
335 335 r = super(DisplayObject, self).__repr__()
336 336 return r
337 337
338 338 def _check_data(self):
339 339 """Override in subclasses if there's something to check."""
340 340 pass
341 341
342 342 def _data_and_metadata(self):
343 343 """shortcut for returning metadata with shape information, if defined"""
344 344 if self.metadata:
345 345 return self.data, deepcopy(self.metadata)
346 346 else:
347 347 return self.data
348 348
349 349 def reload(self):
350 350 """Reload the raw data from file or URL."""
351 351 if self.filename is not None:
352 352 encoding = None if "b" in self._read_flags else "utf-8"
353 353 with open(self.filename, self._read_flags, encoding=encoding) as f:
354 354 self.data = f.read()
355 355 elif self.url is not None:
356 356 # Deferred import
357 357 from urllib.request import urlopen
358 358 response = urlopen(self.url)
359 359 data = response.read()
360 360 # extract encoding from header, if there is one:
361 361 encoding = None
362 362 if 'content-type' in response.headers:
363 363 for sub in response.headers['content-type'].split(';'):
364 364 sub = sub.strip()
365 365 if sub.startswith('charset'):
366 366 encoding = sub.split('=')[-1].strip()
367 367 break
368 368 if 'content-encoding' in response.headers:
369 369 # TODO: do deflate?
370 370 if 'gzip' in response.headers['content-encoding']:
371 371 import gzip
372 372 from io import BytesIO
373 373
374 374 # assume utf-8 if encoding is not specified
375 375 with gzip.open(
376 376 BytesIO(data), "rt", encoding=encoding or "utf-8"
377 377 ) as fp:
378 378 encoding = None
379 379 data = fp.read()
380 380
381 381 # decode data, if an encoding was specified
382 382 # We only touch self.data once since
383 383 # subclasses such as SVG have @data.setter methods
384 384 # that transform self.data into ... well svg.
385 385 if encoding:
386 386 self.data = data.decode(encoding, 'replace')
387 387 else:
388 388 self.data = data
389 389
390 390
391 391 class TextDisplayObject(DisplayObject):
392 392 """Validate that display data is text"""
393 393 def _check_data(self):
394 394 if self.data is not None and not isinstance(self.data, str):
395 395 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
396 396
397 397 class Pretty(TextDisplayObject):
398 398
399 399 def _repr_pretty_(self, pp, cycle):
400 400 return pp.text(self.data)
401 401
402 402
403 403 class HTML(TextDisplayObject):
404 404
405 405 def __init__(self, data=None, url=None, filename=None, metadata=None):
406 406 def warn():
407 407 if not data:
408 408 return False
409 409
410 410 #
411 411 # Avoid calling lower() on the entire data, because it could be a
412 412 # long string and we're only interested in its beginning and end.
413 413 #
414 414 prefix = data[:10].lower()
415 415 suffix = data[-10:].lower()
416 416 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
417 417
418 418 if warn():
419 419 warnings.warn("Consider using IPython.display.IFrame instead")
420 420 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
421 421
422 422 def _repr_html_(self):
423 423 return self._data_and_metadata()
424 424
425 425 def __html__(self):
426 426 """
427 427 This method exists to inform other HTML-using modules (e.g. Markupsafe,
428 428 htmltag, etc) that this object is HTML and does not need things like
429 429 special characters (<>&) escaped.
430 430 """
431 431 return self._repr_html_()
432 432
433 433
434 434 class Markdown(TextDisplayObject):
435 435
436 436 def _repr_markdown_(self):
437 437 return self._data_and_metadata()
438 438
439 439
440 440 class Math(TextDisplayObject):
441 441
442 442 def _repr_latex_(self):
443 443 s = r"$\displaystyle %s$" % self.data.strip('$')
444 444 if self.metadata:
445 445 return s, deepcopy(self.metadata)
446 446 else:
447 447 return s
448 448
449 449
450 450 class Latex(TextDisplayObject):
451 451
452 452 def _repr_latex_(self):
453 453 return self._data_and_metadata()
454 454
455 455
456 456 class SVG(DisplayObject):
457 457 """Embed an SVG into the display.
458 458
459 459 Note if you just want to view a svg image via a URL use `:class:Image` with
460 460 a url=URL keyword argument.
461 461 """
462 462
463 463 _read_flags = 'rb'
464 464 # wrap data in a property, which extracts the <svg> tag, discarding
465 465 # document headers
466 466 _data = None
467 467
468 468 @property
469 469 def data(self):
470 470 return self._data
471 471
472 472 @data.setter
473 473 def data(self, svg):
474 474 if svg is None:
475 475 self._data = None
476 476 return
477 477 # parse into dom object
478 478 from xml.dom import minidom
479 479 x = minidom.parseString(svg)
480 480 # get svg tag (should be 1)
481 481 found_svg = x.getElementsByTagName('svg')
482 482 if found_svg:
483 483 svg = found_svg[0].toxml()
484 484 else:
485 485 # fallback on the input, trust the user
486 486 # but this is probably an error.
487 487 pass
488 488 svg = cast_unicode(svg)
489 489 self._data = svg
490 490
491 491 def _repr_svg_(self):
492 492 return self._data_and_metadata()
493 493
494 494 class ProgressBar(DisplayObject):
495 495 """Progressbar supports displaying a progressbar like element
496 496 """
497 497 def __init__(self, total):
498 498 """Creates a new progressbar
499 499
500 500 Parameters
501 501 ----------
502 502 total : int
503 503 maximum size of the progressbar
504 504 """
505 505 self.total = total
506 506 self._progress = 0
507 507 self.html_width = '60ex'
508 508 self.text_width = 60
509 509 self._display_id = hexlify(os.urandom(8)).decode('ascii')
510 510
511 511 def __repr__(self):
512 512 fraction = self.progress / self.total
513 513 filled = '=' * int(fraction * self.text_width)
514 514 rest = ' ' * (self.text_width - len(filled))
515 515 return '[{}{}] {}/{}'.format(
516 516 filled, rest,
517 517 self.progress, self.total,
518 518 )
519 519
520 520 def _repr_html_(self):
521 521 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
522 522 self.html_width, self.total, self.progress)
523 523
524 524 def display(self):
525 525 display_functions.display(self, display_id=self._display_id)
526 526
527 527 def update(self):
528 528 display_functions.display(self, display_id=self._display_id, update=True)
529 529
530 530 @property
531 531 def progress(self):
532 532 return self._progress
533 533
534 534 @progress.setter
535 535 def progress(self, value):
536 536 self._progress = value
537 537 self.update()
538 538
539 539 def __iter__(self):
540 540 self.display()
541 541 self._progress = -1 # First iteration is 0
542 542 return self
543 543
544 544 def __next__(self):
545 545 """Returns current value and increments display by one."""
546 546 self.progress += 1
547 547 if self.progress < self.total:
548 548 return self.progress
549 549 else:
550 550 raise StopIteration()
551 551
552 552 class JSON(DisplayObject):
553 553 """JSON expects a JSON-able dict or list
554 554
555 555 not an already-serialized JSON string.
556 556
557 557 Scalar types (None, number, string) are not allowed, only dict or list containers.
558 558 """
559 559 # wrap data in a property, which warns about passing already-serialized JSON
560 560 _data = None
561 561 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
562 562 """Create a JSON display object given raw data.
563 563
564 564 Parameters
565 565 ----------
566 566 data : dict or list
567 567 JSON data to display. Not an already-serialized JSON string.
568 568 Scalar types (None, number, string) are not allowed, only dict
569 569 or list containers.
570 570 url : unicode
571 571 A URL to download the data from.
572 572 filename : unicode
573 573 Path to a local file to load the data from.
574 574 expanded : boolean
575 575 Metadata to control whether a JSON display component is expanded.
576 576 metadata : dict
577 577 Specify extra metadata to attach to the json display object.
578 578 root : str
579 579 The name of the root element of the JSON tree
580 580 """
581 581 self.metadata = {
582 582 'expanded': expanded,
583 583 'root': root,
584 584 }
585 585 if metadata:
586 586 self.metadata.update(metadata)
587 587 if kwargs:
588 588 self.metadata.update(kwargs)
589 589 super(JSON, self).__init__(data=data, url=url, filename=filename)
590 590
591 591 def _check_data(self):
592 592 if self.data is not None and not isinstance(self.data, (dict, list)):
593 593 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
594 594
595 595 @property
596 596 def data(self):
597 597 return self._data
598 598
599 599 @data.setter
600 600 def data(self, data):
601 601 if isinstance(data, (Path, PurePath)):
602 602 data = str(data)
603 603
604 604 if isinstance(data, str):
605 605 if self.filename is None and self.url is None:
606 606 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
607 607 data = json.loads(data)
608 608 self._data = data
609 609
610 610 def _data_and_metadata(self):
611 611 return self.data, self.metadata
612 612
613 613 def _repr_json_(self):
614 614 return self._data_and_metadata()
615 615
616 616 _css_t = """var link = document.createElement("link");
617 617 link.ref = "stylesheet";
618 618 link.type = "text/css";
619 619 link.href = "%s";
620 620 document.head.appendChild(link);
621 621 """
622 622
623 623 _lib_t1 = """new Promise(function(resolve, reject) {
624 624 var script = document.createElement("script");
625 625 script.onload = resolve;
626 626 script.onerror = reject;
627 627 script.src = "%s";
628 628 document.head.appendChild(script);
629 629 }).then(() => {
630 630 """
631 631
632 632 _lib_t2 = """
633 633 });"""
634 634
635 635 class GeoJSON(JSON):
636 636 """GeoJSON expects JSON-able dict
637 637
638 638 not an already-serialized JSON string.
639 639
640 640 Scalar types (None, number, string) are not allowed, only dict containers.
641 641 """
642 642
643 643 def __init__(self, *args, **kwargs):
644 644 """Create a GeoJSON display object given raw data.
645 645
646 646 Parameters
647 647 ----------
648 648 data : dict or list
649 649 VegaLite data. Not an already-serialized JSON string.
650 650 Scalar types (None, number, string) are not allowed, only dict
651 651 or list containers.
652 652 url_template : string
653 653 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
654 654 layer_options : dict
655 655 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
656 656 url : unicode
657 657 A URL to download the data from.
658 658 filename : unicode
659 659 Path to a local file to load the data from.
660 660 metadata : dict
661 661 Specify extra metadata to attach to the json display object.
662 662
663 663 Examples
664 664 --------
665 665 The following will display an interactive map of Mars with a point of
666 666 interest on frontend that do support GeoJSON display.
667 667
668 668 >>> from IPython.display import GeoJSON
669 669
670 670 >>> GeoJSON(data={
671 671 ... "type": "Feature",
672 672 ... "geometry": {
673 673 ... "type": "Point",
674 674 ... "coordinates": [-81.327, 296.038]
675 675 ... }
676 676 ... },
677 677 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
678 678 ... layer_options={
679 679 ... "basemap_id": "celestia_mars-shaded-16k_global",
680 680 ... "attribution" : "Celestia/praesepe",
681 681 ... "minZoom" : 0,
682 682 ... "maxZoom" : 18,
683 683 ... })
684 684 <IPython.core.display.GeoJSON object>
685 685
686 686 In the terminal IPython, you will only see the text representation of
687 687 the GeoJSON object.
688 688
689 689 """
690 690
691 691 super(GeoJSON, self).__init__(*args, **kwargs)
692 692
693 693
694 694 def _ipython_display_(self):
695 695 bundle = {
696 696 'application/geo+json': self.data,
697 697 'text/plain': '<IPython.display.GeoJSON object>'
698 698 }
699 699 metadata = {
700 700 'application/geo+json': self.metadata
701 701 }
702 702 display_functions.display(bundle, metadata=metadata, raw=True)
703 703
704 704 class Javascript(TextDisplayObject):
705 705
706 706 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
707 707 """Create a Javascript display object given raw data.
708 708
709 709 When this object is returned by an expression or passed to the
710 710 display function, it will result in the data being displayed
711 711 in the frontend. If the data is a URL, the data will first be
712 712 downloaded and then displayed.
713 713
714 714 In the Notebook, the containing element will be available as `element`,
715 715 and jQuery will be available. Content appended to `element` will be
716 716 visible in the output area.
717 717
718 718 Parameters
719 719 ----------
720 720 data : unicode, str or bytes
721 721 The Javascript source code or a URL to download it from.
722 722 url : unicode
723 723 A URL to download the data from.
724 724 filename : unicode
725 725 Path to a local file to load the data from.
726 726 lib : list or str
727 727 A sequence of Javascript library URLs to load asynchronously before
728 728 running the source code. The full URLs of the libraries should
729 729 be given. A single Javascript library URL can also be given as a
730 730 string.
731 731 css : list or str
732 732 A sequence of css files to load before running the source code.
733 733 The full URLs of the css files should be given. A single css URL
734 734 can also be given as a string.
735 735 """
736 736 if isinstance(lib, str):
737 737 lib = [lib]
738 738 elif lib is None:
739 739 lib = []
740 740 if isinstance(css, str):
741 741 css = [css]
742 742 elif css is None:
743 743 css = []
744 744 if not isinstance(lib, (list,tuple)):
745 745 raise TypeError('expected sequence, got: %r' % lib)
746 746 if not isinstance(css, (list,tuple)):
747 747 raise TypeError('expected sequence, got: %r' % css)
748 748 self.lib = lib
749 749 self.css = css
750 750 super(Javascript, self).__init__(data=data, url=url, filename=filename)
751 751
752 752 def _repr_javascript_(self):
753 753 r = ''
754 754 for c in self.css:
755 755 r += _css_t % c
756 756 for l in self.lib:
757 757 r += _lib_t1 % l
758 758 r += self.data
759 759 r += _lib_t2*len(self.lib)
760 760 return r
761 761
762 762 # constants for identifying png/jpeg data
763 763 _PNG = b'\x89PNG\r\n\x1a\n'
764 764 _JPEG = b'\xff\xd8'
765 765
766 766 def _pngxy(data):
767 767 """read the (width, height) from a PNG header"""
768 768 ihdr = data.index(b'IHDR')
769 769 # next 8 bytes are width/height
770 770 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
771 771
772 772 def _jpegxy(data):
773 773 """read the (width, height) from a JPEG header"""
774 774 # adapted from http://www.64lines.com/jpeg-width-height
775 775
776 776 idx = 4
777 777 while True:
778 778 block_size = struct.unpack('>H', data[idx:idx+2])[0]
779 779 idx = idx + block_size
780 780 if data[idx:idx+2] == b'\xFF\xC0':
781 781 # found Start of Frame
782 782 iSOF = idx
783 783 break
784 784 else:
785 785 # read another block
786 786 idx += 2
787 787
788 788 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
789 789 return w, h
790 790
791 791 def _gifxy(data):
792 792 """read the (width, height) from a GIF header"""
793 793 return struct.unpack('<HH', data[6:10])
794 794
795 795
796 796 class Image(DisplayObject):
797 797
798 798 _read_flags = 'rb'
799 799 _FMT_JPEG = u'jpeg'
800 800 _FMT_PNG = u'png'
801 801 _FMT_GIF = u'gif'
802 802 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
803 803 _MIMETYPES = {
804 804 _FMT_PNG: 'image/png',
805 805 _FMT_JPEG: 'image/jpeg',
806 806 _FMT_GIF: 'image/gif',
807 807 }
808 808
809 809 def __init__(
810 810 self,
811 811 data=None,
812 812 url=None,
813 813 filename=None,
814 814 format=None,
815 815 embed=None,
816 816 width=None,
817 817 height=None,
818 818 retina=False,
819 819 unconfined=False,
820 820 metadata=None,
821 821 alt=None,
822 822 ):
823 823 """Create a PNG/JPEG/GIF image object given raw data.
824 824
825 825 When this object is returned by an input cell or passed to the
826 826 display function, it will result in the image being displayed
827 827 in the frontend.
828 828
829 829 Parameters
830 830 ----------
831 831 data : unicode, str or bytes
832 832 The raw image data or a URL or filename to load the data from.
833 833 This always results in embedded image data.
834 834
835 835 url : unicode
836 836 A URL to download the data from. If you specify `url=`,
837 837 the image data will not be embedded unless you also specify `embed=True`.
838 838
839 839 filename : unicode
840 840 Path to a local file to load the data from.
841 841 Images from a file are always embedded.
842 842
843 843 format : unicode
844 844 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
845 845 for format will be inferred from the filename extension.
846 846
847 847 embed : bool
848 848 Should the image data be embedded using a data URI (True) or be
849 849 loaded using an <img> tag. Set this to True if you want the image
850 850 to be viewable later with no internet connection in the notebook.
851 851
852 852 Default is `True`, unless the keyword argument `url` is set, then
853 853 default value is `False`.
854 854
855 855 Note that QtConsole is not able to display images if `embed` is set to `False`
856 856
857 857 width : int
858 858 Width in pixels to which to constrain the image in html
859 859
860 860 height : int
861 861 Height in pixels to which to constrain the image in html
862 862
863 863 retina : bool
864 864 Automatically set the width and height to half of the measured
865 865 width and height.
866 866 This only works for embedded images because it reads the width/height
867 867 from image data.
868 868 For non-embedded images, you can just set the desired display width
869 869 and height directly.
870 870
871 871 unconfined : bool
872 872 Set unconfined=True to disable max-width confinement of the image.
873 873
874 874 metadata : dict
875 875 Specify extra metadata to attach to the image.
876 876
877 877 alt : unicode
878 878 Alternative text for the image, for use by screen readers.
879 879
880 880 Examples
881 881 --------
882 882 embedded image data, works in qtconsole and notebook
883 883 when passed positionally, the first arg can be any of raw image data,
884 884 a URL, or a filename from which to load image data.
885 885 The result is always embedding image data for inline images.
886 886
887 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
887 >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP
888 888 <IPython.core.display.Image object>
889 889
890 890 >>> Image('/path/to/image.jpg')
891 891 <IPython.core.display.Image object>
892 892
893 893 >>> Image(b'RAW_PNG_DATA...')
894 894 <IPython.core.display.Image object>
895 895
896 896 Specifying Image(url=...) does not embed the image data,
897 897 it only generates ``<img>`` tag with a link to the source.
898 898 This will not work in the qtconsole or offline.
899 899
900 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
900 >>> Image(url='https://www.google.fr/images/srpr/logo3w.png')
901 901 <IPython.core.display.Image object>
902 902
903 903 """
904 904 if isinstance(data, (Path, PurePath)):
905 905 data = str(data)
906 906
907 907 if filename is not None:
908 908 ext = self._find_ext(filename)
909 909 elif url is not None:
910 910 ext = self._find_ext(url)
911 911 elif data is None:
912 912 raise ValueError("No image data found. Expecting filename, url, or data.")
913 913 elif isinstance(data, str) and (
914 914 data.startswith('http') or _safe_exists(data)
915 915 ):
916 916 ext = self._find_ext(data)
917 917 else:
918 918 ext = None
919 919
920 920 if format is None:
921 921 if ext is not None:
922 922 if ext == u'jpg' or ext == u'jpeg':
923 923 format = self._FMT_JPEG
924 924 elif ext == u'png':
925 925 format = self._FMT_PNG
926 926 elif ext == u'gif':
927 927 format = self._FMT_GIF
928 928 else:
929 929 format = ext.lower()
930 930 elif isinstance(data, bytes):
931 931 # infer image type from image data header,
932 932 # only if format has not been specified.
933 933 if data[:2] == _JPEG:
934 934 format = self._FMT_JPEG
935 935
936 936 # failed to detect format, default png
937 937 if format is None:
938 938 format = self._FMT_PNG
939 939
940 940 if format.lower() == 'jpg':
941 941 # jpg->jpeg
942 942 format = self._FMT_JPEG
943 943
944 944 self.format = format.lower()
945 945 self.embed = embed if embed is not None else (url is None)
946 946
947 947 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
948 948 raise ValueError("Cannot embed the '%s' image format" % (self.format))
949 949 if self.embed:
950 950 self._mimetype = self._MIMETYPES.get(self.format)
951 951
952 952 self.width = width
953 953 self.height = height
954 954 self.retina = retina
955 955 self.unconfined = unconfined
956 956 self.alt = alt
957 957 super(Image, self).__init__(data=data, url=url, filename=filename,
958 958 metadata=metadata)
959 959
960 960 if self.width is None and self.metadata.get('width', {}):
961 961 self.width = metadata['width']
962 962
963 963 if self.height is None and self.metadata.get('height', {}):
964 964 self.height = metadata['height']
965 965
966 966 if self.alt is None and self.metadata.get("alt", {}):
967 967 self.alt = metadata["alt"]
968 968
969 969 if retina:
970 970 self._retina_shape()
971 971
972 972
973 973 def _retina_shape(self):
974 974 """load pixel-doubled width and height from image data"""
975 975 if not self.embed:
976 976 return
977 977 if self.format == self._FMT_PNG:
978 978 w, h = _pngxy(self.data)
979 979 elif self.format == self._FMT_JPEG:
980 980 w, h = _jpegxy(self.data)
981 981 elif self.format == self._FMT_GIF:
982 982 w, h = _gifxy(self.data)
983 983 else:
984 984 # retina only supports png
985 985 return
986 986 self.width = w // 2
987 987 self.height = h // 2
988 988
989 989 def reload(self):
990 990 """Reload the raw data from file or URL."""
991 991 if self.embed:
992 992 super(Image,self).reload()
993 993 if self.retina:
994 994 self._retina_shape()
995 995
996 996 def _repr_html_(self):
997 997 if not self.embed:
998 998 width = height = klass = alt = ""
999 999 if self.width:
1000 1000 width = ' width="%d"' % self.width
1001 1001 if self.height:
1002 1002 height = ' height="%d"' % self.height
1003 1003 if self.unconfined:
1004 1004 klass = ' class="unconfined"'
1005 1005 if self.alt:
1006 1006 alt = ' alt="%s"' % html.escape(self.alt)
1007 1007 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
1008 1008 url=self.url,
1009 1009 width=width,
1010 1010 height=height,
1011 1011 klass=klass,
1012 1012 alt=alt,
1013 1013 )
1014 1014
1015 1015 def _repr_mimebundle_(self, include=None, exclude=None):
1016 1016 """Return the image as a mimebundle
1017 1017
1018 1018 Any new mimetype support should be implemented here.
1019 1019 """
1020 1020 if self.embed:
1021 1021 mimetype = self._mimetype
1022 1022 data, metadata = self._data_and_metadata(always_both=True)
1023 1023 if metadata:
1024 1024 metadata = {mimetype: metadata}
1025 1025 return {mimetype: data}, metadata
1026 1026 else:
1027 1027 return {'text/html': self._repr_html_()}
1028 1028
1029 1029 def _data_and_metadata(self, always_both=False):
1030 1030 """shortcut for returning metadata with shape information, if defined"""
1031 1031 try:
1032 1032 b64_data = b2a_base64(self.data).decode('ascii')
1033 1033 except TypeError as e:
1034 1034 raise FileNotFoundError(
1035 1035 "No such file or directory: '%s'" % (self.data)) from e
1036 1036 md = {}
1037 1037 if self.metadata:
1038 1038 md.update(self.metadata)
1039 1039 if self.width:
1040 1040 md['width'] = self.width
1041 1041 if self.height:
1042 1042 md['height'] = self.height
1043 1043 if self.unconfined:
1044 1044 md['unconfined'] = self.unconfined
1045 1045 if self.alt:
1046 1046 md["alt"] = self.alt
1047 1047 if md or always_both:
1048 1048 return b64_data, md
1049 1049 else:
1050 1050 return b64_data
1051 1051
1052 1052 def _repr_png_(self):
1053 1053 if self.embed and self.format == self._FMT_PNG:
1054 1054 return self._data_and_metadata()
1055 1055
1056 1056 def _repr_jpeg_(self):
1057 1057 if self.embed and self.format == self._FMT_JPEG:
1058 1058 return self._data_and_metadata()
1059 1059
1060 1060 def _find_ext(self, s):
1061 1061 base, ext = splitext(s)
1062 1062
1063 1063 if not ext:
1064 1064 return base
1065 1065
1066 1066 # `splitext` includes leading period, so we skip it
1067 1067 return ext[1:].lower()
1068 1068
1069 1069
1070 1070 class Video(DisplayObject):
1071 1071
1072 1072 def __init__(self, data=None, url=None, filename=None, embed=False,
1073 1073 mimetype=None, width=None, height=None, html_attributes="controls"):
1074 1074 """Create a video object given raw data or an URL.
1075 1075
1076 1076 When this object is returned by an input cell or passed to the
1077 1077 display function, it will result in the video being displayed
1078 1078 in the frontend.
1079 1079
1080 1080 Parameters
1081 1081 ----------
1082 1082 data : unicode, str or bytes
1083 1083 The raw video data or a URL or filename to load the data from.
1084 1084 Raw data will require passing ``embed=True``.
1085 1085
1086 1086 url : unicode
1087 1087 A URL for the video. If you specify ``url=``,
1088 1088 the image data will not be embedded.
1089 1089
1090 1090 filename : unicode
1091 1091 Path to a local file containing the video.
1092 1092 Will be interpreted as a local URL unless ``embed=True``.
1093 1093
1094 1094 embed : bool
1095 1095 Should the video be embedded using a data URI (True) or be
1096 1096 loaded using a <video> tag (False).
1097 1097
1098 1098 Since videos are large, embedding them should be avoided, if possible.
1099 1099 You must confirm embedding as your intention by passing ``embed=True``.
1100 1100
1101 1101 Local files can be displayed with URLs without embedding the content, via::
1102 1102
1103 1103 Video('./video.mp4')
1104 1104
1105 1105 mimetype : unicode
1106 1106 Specify the mimetype for embedded videos.
1107 1107 Default will be guessed from file extension, if available.
1108 1108
1109 1109 width : int
1110 1110 Width in pixels to which to constrain the video in HTML.
1111 1111 If not supplied, defaults to the width of the video.
1112 1112
1113 1113 height : int
1114 1114 Height in pixels to which to constrain the video in html.
1115 1115 If not supplied, defaults to the height of the video.
1116 1116
1117 1117 html_attributes : str
1118 1118 Attributes for the HTML ``<video>`` block.
1119 1119 Default: ``"controls"`` to get video controls.
1120 1120 Other examples: ``"controls muted"`` for muted video with controls,
1121 1121 ``"loop autoplay"`` for looping autoplaying video without controls.
1122 1122
1123 1123 Examples
1124 1124 --------
1125 1125 ::
1126 1126
1127 1127 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1128 1128 Video('path/to/video.mp4')
1129 1129 Video('path/to/video.mp4', embed=True)
1130 1130 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1131 1131 Video(b'raw-videodata', embed=True)
1132 1132 """
1133 1133 if isinstance(data, (Path, PurePath)):
1134 1134 data = str(data)
1135 1135
1136 1136 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1137 1137 url = data
1138 1138 data = None
1139 1139 elif data is not None and os.path.exists(data):
1140 1140 filename = data
1141 1141 data = None
1142 1142
1143 1143 if data and not embed:
1144 1144 msg = ''.join([
1145 1145 "To embed videos, you must pass embed=True ",
1146 1146 "(this may make your notebook files huge)\n",
1147 1147 "Consider passing Video(url='...')",
1148 1148 ])
1149 1149 raise ValueError(msg)
1150 1150
1151 1151 self.mimetype = mimetype
1152 1152 self.embed = embed
1153 1153 self.width = width
1154 1154 self.height = height
1155 1155 self.html_attributes = html_attributes
1156 1156 super(Video, self).__init__(data=data, url=url, filename=filename)
1157 1157
1158 1158 def _repr_html_(self):
1159 1159 width = height = ''
1160 1160 if self.width:
1161 1161 width = ' width="%d"' % self.width
1162 1162 if self.height:
1163 1163 height = ' height="%d"' % self.height
1164 1164
1165 1165 # External URLs and potentially local files are not embedded into the
1166 1166 # notebook output.
1167 1167 if not self.embed:
1168 1168 url = self.url if self.url is not None else self.filename
1169 1169 output = """<video src="{0}" {1} {2} {3}>
1170 1170 Your browser does not support the <code>video</code> element.
1171 1171 </video>""".format(url, self.html_attributes, width, height)
1172 1172 return output
1173 1173
1174 1174 # Embedded videos are base64-encoded.
1175 1175 mimetype = self.mimetype
1176 1176 if self.filename is not None:
1177 1177 if not mimetype:
1178 1178 mimetype, _ = mimetypes.guess_type(self.filename)
1179 1179
1180 1180 with open(self.filename, 'rb') as f:
1181 1181 video = f.read()
1182 1182 else:
1183 1183 video = self.data
1184 1184 if isinstance(video, str):
1185 1185 # unicode input is already b64-encoded
1186 1186 b64_video = video
1187 1187 else:
1188 1188 b64_video = b2a_base64(video).decode('ascii').rstrip()
1189 1189
1190 1190 output = """<video {0} {1} {2}>
1191 1191 <source src="data:{3};base64,{4}" type="{3}">
1192 1192 Your browser does not support the video tag.
1193 1193 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1194 1194 return output
1195 1195
1196 1196 def reload(self):
1197 1197 # TODO
1198 1198 pass
1199 1199
1200 1200
1201 1201 @skip_doctest
1202 1202 def set_matplotlib_formats(*formats, **kwargs):
1203 1203 """
1204 1204 .. deprecated:: 7.23
1205 1205
1206 1206 use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
1207 1207
1208 1208 Select figure formats for the inline backend. Optionally pass quality for JPEG.
1209 1209
1210 1210 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1211 1211
1212 1212 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1213 1213
1214 1214 To set this in your config files use the following::
1215 1215
1216 1216 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1217 1217 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1218 1218
1219 1219 Parameters
1220 1220 ----------
1221 1221 *formats : strs
1222 1222 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1223 1223 **kwargs
1224 1224 Keyword args will be relayed to ``figure.canvas.print_figure``.
1225 1225 """
1226 1226 warnings.warn(
1227 1227 "`set_matplotlib_formats` is deprecated since IPython 7.23, directly "
1228 1228 "use `matplotlib_inline.backend_inline.set_matplotlib_formats()`",
1229 1229 DeprecationWarning,
1230 1230 stacklevel=2,
1231 1231 )
1232 1232
1233 1233 from matplotlib_inline.backend_inline import (
1234 1234 set_matplotlib_formats as set_matplotlib_formats_orig,
1235 1235 )
1236 1236
1237 1237 set_matplotlib_formats_orig(*formats, **kwargs)
1238 1238
1239 1239 @skip_doctest
1240 1240 def set_matplotlib_close(close=True):
1241 1241 """
1242 1242 .. deprecated:: 7.23
1243 1243
1244 1244 use `matplotlib_inline.backend_inline.set_matplotlib_close()`
1245 1245
1246 1246 Set whether the inline backend closes all figures automatically or not.
1247 1247
1248 1248 By default, the inline backend used in the IPython Notebook will close all
1249 1249 matplotlib figures automatically after each cell is run. This means that
1250 1250 plots in different cells won't interfere. Sometimes, you may want to make
1251 1251 a plot in one cell and then refine it in later cells. This can be accomplished
1252 1252 by::
1253 1253
1254 1254 In [1]: set_matplotlib_close(False)
1255 1255
1256 1256 To set this in your config files use the following::
1257 1257
1258 1258 c.InlineBackend.close_figures = False
1259 1259
1260 1260 Parameters
1261 1261 ----------
1262 1262 close : bool
1263 1263 Should all matplotlib figures be automatically closed after each cell is
1264 1264 run?
1265 1265 """
1266 1266 warnings.warn(
1267 1267 "`set_matplotlib_close` is deprecated since IPython 7.23, directly "
1268 1268 "use `matplotlib_inline.backend_inline.set_matplotlib_close()`",
1269 1269 DeprecationWarning,
1270 1270 stacklevel=2,
1271 1271 )
1272 1272
1273 1273 from matplotlib_inline.backend_inline import (
1274 1274 set_matplotlib_close as set_matplotlib_close_orig,
1275 1275 )
1276 1276
1277 1277 set_matplotlib_close_orig(close)
@@ -1,854 +1,854 b''
1 1 """Implementation of magic functions for interaction with the OS.
2 2
3 3 Note: this module is named 'osm' instead of 'os' to avoid a collision with the
4 4 builtin.
5 5 """
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import io
10 10 import os
11 11 import re
12 12 import sys
13 13 from pprint import pformat
14 14
15 15 from IPython.core import magic_arguments
16 16 from IPython.core import oinspect
17 17 from IPython.core import page
18 18 from IPython.core.alias import AliasError, Alias
19 19 from IPython.core.error import UsageError
20 20 from IPython.core.magic import (
21 21 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
22 22 )
23 23 from IPython.testing.skipdoctest import skip_doctest
24 24 from IPython.utils.openpy import source_to_unicode
25 25 from IPython.utils.process import abbrev_cwd
26 26 from IPython.utils.terminal import set_term_title
27 27 from traitlets import Bool
28 28 from warnings import warn
29 29
30 30
31 31 @magics_class
32 32 class OSMagics(Magics):
33 33 """Magics to interact with the underlying OS (shell-type functionality).
34 34 """
35 35
36 36 cd_force_quiet = Bool(False,
37 37 help="Force %cd magic to be quiet even if -q is not passed."
38 38 ).tag(config=True)
39 39
40 40 def __init__(self, shell=None, **kwargs):
41 41
42 42 # Now define isexec in a cross platform manner.
43 43 self.is_posix = False
44 44 self.execre = None
45 45 if os.name == 'posix':
46 46 self.is_posix = True
47 47 else:
48 48 try:
49 49 winext = os.environ['pathext'].replace(';','|').replace('.','')
50 50 except KeyError:
51 51 winext = 'exe|com|bat|py'
52 52 try:
53 53 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
54 54 except re.error:
55 55 warn("Seems like your pathext environmental "
56 56 "variable is malformed. Please check it to "
57 57 "enable a proper handle of file extensions "
58 58 "managed for your system")
59 59 winext = 'exe|com|bat|py'
60 60 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
61 61
62 62 # call up the chain
63 63 super().__init__(shell=shell, **kwargs)
64 64
65 65
66 66 def _isexec_POSIX(self, file):
67 67 """
68 68 Test for executable on a POSIX system
69 69 """
70 70 if os.access(file.path, os.X_OK):
71 71 # will fail on maxOS if access is not X_OK
72 72 return file.is_file()
73 73 return False
74 74
75 75
76 76
77 77 def _isexec_WIN(self, file):
78 78 """
79 79 Test for executable file on non POSIX system
80 80 """
81 81 return file.is_file() and self.execre.match(file.name) is not None
82 82
83 83 def isexec(self, file):
84 84 """
85 85 Test for executable file on non POSIX system
86 86 """
87 87 if self.is_posix:
88 88 return self._isexec_POSIX(file)
89 89 else:
90 90 return self._isexec_WIN(file)
91 91
92 92
93 93 @skip_doctest
94 94 @line_magic
95 95 def alias(self, parameter_s=''):
96 96 """Define an alias for a system command.
97 97
98 98 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
99 99
100 100 Then, typing 'alias_name params' will execute the system command 'cmd
101 101 params' (from your underlying operating system).
102 102
103 103 Aliases have lower precedence than magic functions and Python normal
104 104 variables, so if 'foo' is both a Python variable and an alias, the
105 105 alias can not be executed until 'del foo' removes the Python variable.
106 106
107 107 You can use the %l specifier in an alias definition to represent the
108 108 whole line when the alias is called. For example::
109 109
110 110 In [2]: alias bracket echo "Input in brackets: <%l>"
111 111 In [3]: bracket hello world
112 112 Input in brackets: <hello world>
113 113
114 114 You can also define aliases with parameters using %s specifiers (one
115 115 per parameter)::
116 116
117 117 In [1]: alias parts echo first %s second %s
118 118 In [2]: %parts A B
119 119 first A second B
120 120 In [3]: %parts A
121 121 Incorrect number of arguments: 2 expected.
122 122 parts is an alias to: 'echo first %s second %s'
123 123
124 124 Note that %l and %s are mutually exclusive. You can only use one or
125 125 the other in your aliases.
126 126
127 127 Aliases expand Python variables just like system calls using ! or !!
128 128 do: all expressions prefixed with '$' get expanded. For details of
129 129 the semantic rules, see PEP-215:
130 http://www.python.org/peps/pep-0215.html. This is the library used by
130 https://www.python.org/dev/peps/pep-0215/. This is the library used by
131 131 IPython for variable expansion. If you want to access a true shell
132 132 variable, an extra $ is necessary to prevent its expansion by
133 133 IPython::
134 134
135 135 In [6]: alias show echo
136 136 In [7]: PATH='A Python string'
137 137 In [8]: show $PATH
138 138 A Python string
139 139 In [9]: show $$PATH
140 140 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
141 141
142 142 You can use the alias facility to access all of $PATH. See the %rehashx
143 143 function, which automatically creates aliases for the contents of your
144 144 $PATH.
145 145
146 146 If called with no parameters, %alias prints the current alias table
147 147 for your system. For posix systems, the default aliases are 'cat',
148 148 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
149 149 aliases are added. For windows-based systems, the default aliases are
150 150 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
151 151
152 152 You can see the definition of alias by adding a question mark in the
153 153 end::
154 154
155 155 In [1]: cat?
156 156 Repr: <alias cat for 'cat'>"""
157 157
158 158 par = parameter_s.strip()
159 159 if not par:
160 160 aliases = sorted(self.shell.alias_manager.aliases)
161 161 # stored = self.shell.db.get('stored_aliases', {} )
162 162 # for k, v in stored:
163 163 # atab.append(k, v[0])
164 164
165 165 print("Total number of aliases:", len(aliases))
166 166 sys.stdout.flush()
167 167 return aliases
168 168
169 169 # Now try to define a new one
170 170 try:
171 171 alias,cmd = par.split(None, 1)
172 172 except TypeError:
173 173 print(oinspect.getdoc(self.alias))
174 174 return
175 175
176 176 try:
177 177 self.shell.alias_manager.define_alias(alias, cmd)
178 178 except AliasError as e:
179 179 print(e)
180 180 # end magic_alias
181 181
182 182 @line_magic
183 183 def unalias(self, parameter_s=''):
184 184 """Remove an alias"""
185 185
186 186 aname = parameter_s.strip()
187 187 try:
188 188 self.shell.alias_manager.undefine_alias(aname)
189 189 except ValueError as e:
190 190 print(e)
191 191 return
192 192
193 193 stored = self.shell.db.get('stored_aliases', {} )
194 194 if aname in stored:
195 195 print("Removing %stored alias",aname)
196 196 del stored[aname]
197 197 self.shell.db['stored_aliases'] = stored
198 198
199 199 @line_magic
200 200 def rehashx(self, parameter_s=''):
201 201 """Update the alias table with all executable files in $PATH.
202 202
203 203 rehashx explicitly checks that every entry in $PATH is a file
204 204 with execute access (os.X_OK).
205 205
206 206 Under Windows, it checks executability as a match against a
207 207 '|'-separated string of extensions, stored in the IPython config
208 208 variable win_exec_ext. This defaults to 'exe|com|bat'.
209 209
210 210 This function also resets the root module cache of module completer,
211 211 used on slow filesystems.
212 212 """
213 213 from IPython.core.alias import InvalidAliasError
214 214
215 215 # for the benefit of module completer in ipy_completers.py
216 216 del self.shell.db['rootmodules_cache']
217 217
218 218 path = [os.path.abspath(os.path.expanduser(p)) for p in
219 219 os.environ.get('PATH','').split(os.pathsep)]
220 220
221 221 syscmdlist = []
222 222 savedir = os.getcwd()
223 223
224 224 # Now walk the paths looking for executables to alias.
225 225 try:
226 226 # write the whole loop for posix/Windows so we don't have an if in
227 227 # the innermost part
228 228 if self.is_posix:
229 229 for pdir in path:
230 230 try:
231 231 os.chdir(pdir)
232 232 except OSError:
233 233 continue
234 234
235 235 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
236 236 dirlist = os.scandir(path=pdir)
237 237 for ff in dirlist:
238 238 if self.isexec(ff):
239 239 fname = ff.name
240 240 try:
241 241 # Removes dots from the name since ipython
242 242 # will assume names with dots to be python.
243 243 if not self.shell.alias_manager.is_alias(fname):
244 244 self.shell.alias_manager.define_alias(
245 245 fname.replace('.',''), fname)
246 246 except InvalidAliasError:
247 247 pass
248 248 else:
249 249 syscmdlist.append(fname)
250 250 else:
251 251 no_alias = Alias.blacklist
252 252 for pdir in path:
253 253 try:
254 254 os.chdir(pdir)
255 255 except OSError:
256 256 continue
257 257
258 258 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
259 259 dirlist = os.scandir(pdir)
260 260 for ff in dirlist:
261 261 fname = ff.name
262 262 base, ext = os.path.splitext(fname)
263 263 if self.isexec(ff) and base.lower() not in no_alias:
264 264 if ext.lower() == '.exe':
265 265 fname = base
266 266 try:
267 267 # Removes dots from the name since ipython
268 268 # will assume names with dots to be python.
269 269 self.shell.alias_manager.define_alias(
270 270 base.lower().replace('.',''), fname)
271 271 except InvalidAliasError:
272 272 pass
273 273 syscmdlist.append(fname)
274 274
275 275 self.shell.db['syscmdlist'] = syscmdlist
276 276 finally:
277 277 os.chdir(savedir)
278 278
279 279 @skip_doctest
280 280 @line_magic
281 281 def pwd(self, parameter_s=''):
282 282 """Return the current working directory path.
283 283
284 284 Examples
285 285 --------
286 286 ::
287 287
288 288 In [9]: pwd
289 289 Out[9]: '/home/tsuser/sprint/ipython'
290 290 """
291 291 try:
292 292 return os.getcwd()
293 293 except FileNotFoundError as e:
294 294 raise UsageError("CWD no longer exists - please use %cd to change directory.") from e
295 295
296 296 @skip_doctest
297 297 @line_magic
298 298 def cd(self, parameter_s=''):
299 299 """Change the current working directory.
300 300
301 301 This command automatically maintains an internal list of directories
302 302 you visit during your IPython session, in the variable ``_dh``. The
303 303 command :magic:`%dhist` shows this history nicely formatted. You can
304 304 also do ``cd -<tab>`` to see directory history conveniently.
305 305 Usage:
306 306
307 307 - ``cd 'dir'``: changes to directory 'dir'.
308 308 - ``cd -``: changes to the last visited directory.
309 309 - ``cd -<n>``: changes to the n-th directory in the directory history.
310 310 - ``cd --foo``: change to directory that matches 'foo' in history
311 311 - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
312 312 - Hitting a tab key after ``cd -b`` allows you to tab-complete
313 313 bookmark names.
314 314
315 315 .. note::
316 316 ``cd <bookmark_name>`` is enough if there is no directory
317 317 ``<bookmark_name>``, but a bookmark with the name exists.
318 318
319 319 Options:
320 320
321 321 -q Be quiet. Do not print the working directory after the
322 322 cd command is executed. By default IPython's cd
323 323 command does print this directory, since the default
324 324 prompts do not display path information.
325 325
326 326 .. note::
327 327 Note that ``!cd`` doesn't work for this purpose because the shell
328 328 where ``!command`` runs is immediately discarded after executing
329 329 'command'.
330 330
331 331 Examples
332 332 --------
333 333 ::
334 334
335 335 In [10]: cd parent/child
336 336 /home/tsuser/parent/child
337 337 """
338 338
339 339 try:
340 340 oldcwd = os.getcwd()
341 341 except FileNotFoundError:
342 342 # Happens if the CWD has been deleted.
343 343 oldcwd = None
344 344
345 345 numcd = re.match(r'(-)(\d+)$',parameter_s)
346 346 # jump in directory history by number
347 347 if numcd:
348 348 nn = int(numcd.group(2))
349 349 try:
350 350 ps = self.shell.user_ns['_dh'][nn]
351 351 except IndexError:
352 352 print('The requested directory does not exist in history.')
353 353 return
354 354 else:
355 355 opts = {}
356 356 elif parameter_s.startswith('--'):
357 357 ps = None
358 358 fallback = None
359 359 pat = parameter_s[2:]
360 360 dh = self.shell.user_ns['_dh']
361 361 # first search only by basename (last component)
362 362 for ent in reversed(dh):
363 363 if pat in os.path.basename(ent) and os.path.isdir(ent):
364 364 ps = ent
365 365 break
366 366
367 367 if fallback is None and pat in ent and os.path.isdir(ent):
368 368 fallback = ent
369 369
370 370 # if we have no last part match, pick the first full path match
371 371 if ps is None:
372 372 ps = fallback
373 373
374 374 if ps is None:
375 375 print("No matching entry in directory history")
376 376 return
377 377 else:
378 378 opts = {}
379 379
380 380
381 381 else:
382 382 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
383 383 # jump to previous
384 384 if ps == '-':
385 385 try:
386 386 ps = self.shell.user_ns['_dh'][-2]
387 387 except IndexError as e:
388 388 raise UsageError('%cd -: No previous directory to change to.') from e
389 389 # jump to bookmark if needed
390 390 else:
391 391 if not os.path.isdir(ps) or 'b' in opts:
392 392 bkms = self.shell.db.get('bookmarks', {})
393 393
394 394 if ps in bkms:
395 395 target = bkms[ps]
396 396 print('(bookmark:%s) -> %s' % (ps, target))
397 397 ps = target
398 398 else:
399 399 if 'b' in opts:
400 400 raise UsageError("Bookmark '%s' not found. "
401 401 "Use '%%bookmark -l' to see your bookmarks." % ps)
402 402
403 403 # at this point ps should point to the target dir
404 404 if ps:
405 405 try:
406 406 os.chdir(os.path.expanduser(ps))
407 407 if hasattr(self.shell, 'term_title') and self.shell.term_title:
408 408 set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
409 409 except OSError:
410 410 print(sys.exc_info()[1])
411 411 else:
412 412 cwd = os.getcwd()
413 413 dhist = self.shell.user_ns['_dh']
414 414 if oldcwd != cwd:
415 415 dhist.append(cwd)
416 416 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
417 417
418 418 else:
419 419 os.chdir(self.shell.home_dir)
420 420 if hasattr(self.shell, 'term_title') and self.shell.term_title:
421 421 set_term_title(self.shell.term_title_format.format(cwd="~"))
422 422 cwd = os.getcwd()
423 423 dhist = self.shell.user_ns['_dh']
424 424
425 425 if oldcwd != cwd:
426 426 dhist.append(cwd)
427 427 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
428 428 if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']:
429 429 print(self.shell.user_ns['_dh'][-1])
430 430
431 431 @line_magic
432 432 def env(self, parameter_s=''):
433 433 """Get, set, or list environment variables.
434 434
435 435 Usage:\\
436 436
437 437 :``%env``: lists all environment variables/values
438 438 :``%env var``: get value for var
439 439 :``%env var val``: set value for var
440 440 :``%env var=val``: set value for var
441 441 :``%env var=$val``: set value for var, using python expansion if possible
442 442 """
443 443 if parameter_s.strip():
444 444 split = '=' if '=' in parameter_s else ' '
445 445 bits = parameter_s.split(split)
446 446 if len(bits) == 1:
447 447 key = parameter_s.strip()
448 448 if key in os.environ:
449 449 return os.environ[key]
450 450 else:
451 451 err = "Environment does not have key: {0}".format(key)
452 452 raise UsageError(err)
453 453 if len(bits) > 1:
454 454 return self.set_env(parameter_s)
455 455 env = dict(os.environ)
456 456 # hide likely secrets when printing the whole environment
457 457 for key in list(env):
458 458 if any(s in key.lower() for s in ('key', 'token', 'secret')):
459 459 env[key] = '<hidden>'
460 460
461 461 return env
462 462
463 463 @line_magic
464 464 def set_env(self, parameter_s):
465 465 """Set environment variables. Assumptions are that either "val" is a
466 466 name in the user namespace, or val is something that evaluates to a
467 467 string.
468 468
469 469 Usage:\\
470 470 %set_env var val: set value for var
471 471 %set_env var=val: set value for var
472 472 %set_env var=$val: set value for var, using python expansion if possible
473 473 """
474 474 split = '=' if '=' in parameter_s else ' '
475 475 bits = parameter_s.split(split, 1)
476 476 if not parameter_s.strip() or len(bits)<2:
477 477 raise UsageError("usage is 'set_env var=val'")
478 478 var = bits[0].strip()
479 479 val = bits[1].strip()
480 480 if re.match(r'.*\s.*', var):
481 481 # an environment variable with whitespace is almost certainly
482 482 # not what the user intended. what's more likely is the wrong
483 483 # split was chosen, ie for "set_env cmd_args A=B", we chose
484 484 # '=' for the split and should have chosen ' '. to get around
485 485 # this, users should just assign directly to os.environ or use
486 486 # standard magic {var} expansion.
487 487 err = "refusing to set env var with whitespace: '{0}'"
488 488 err = err.format(val)
489 489 raise UsageError(err)
490 490 os.environ[var] = val
491 491 print('env: {0}={1}'.format(var,val))
492 492
493 493 @line_magic
494 494 def pushd(self, parameter_s=''):
495 495 """Place the current dir on stack and change directory.
496 496
497 497 Usage:\\
498 498 %pushd ['dirname']
499 499 """
500 500
501 501 dir_s = self.shell.dir_stack
502 502 tgt = os.path.expanduser(parameter_s)
503 503 cwd = os.getcwd().replace(self.shell.home_dir,'~')
504 504 if tgt:
505 505 self.cd(parameter_s)
506 506 dir_s.insert(0,cwd)
507 507 return self.shell.run_line_magic('dirs', '')
508 508
509 509 @line_magic
510 510 def popd(self, parameter_s=''):
511 511 """Change to directory popped off the top of the stack.
512 512 """
513 513 if not self.shell.dir_stack:
514 514 raise UsageError("%popd on empty stack")
515 515 top = self.shell.dir_stack.pop(0)
516 516 self.cd(top)
517 517 print("popd ->",top)
518 518
519 519 @line_magic
520 520 def dirs(self, parameter_s=''):
521 521 """Return the current directory stack."""
522 522
523 523 return self.shell.dir_stack
524 524
525 525 @line_magic
526 526 def dhist(self, parameter_s=''):
527 527 """Print your history of visited directories.
528 528
529 529 %dhist -> print full history\\
530 530 %dhist n -> print last n entries only\\
531 531 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
532 532
533 533 This history is automatically maintained by the %cd command, and
534 534 always available as the global list variable _dh. You can use %cd -<n>
535 535 to go to directory number <n>.
536 536
537 537 Note that most of time, you should view directory history by entering
538 538 cd -<TAB>.
539 539
540 540 """
541 541
542 542 dh = self.shell.user_ns['_dh']
543 543 if parameter_s:
544 544 try:
545 545 args = map(int,parameter_s.split())
546 546 except:
547 547 self.arg_err(self.dhist)
548 548 return
549 549 if len(args) == 1:
550 550 ini,fin = max(len(dh)-(args[0]),0),len(dh)
551 551 elif len(args) == 2:
552 552 ini,fin = args
553 553 fin = min(fin, len(dh))
554 554 else:
555 555 self.arg_err(self.dhist)
556 556 return
557 557 else:
558 558 ini,fin = 0,len(dh)
559 559 print('Directory history (kept in _dh)')
560 560 for i in range(ini, fin):
561 561 print("%d: %s" % (i, dh[i]))
562 562
563 563 @skip_doctest
564 564 @line_magic
565 565 def sc(self, parameter_s=''):
566 566 """Shell capture - run shell command and capture output (DEPRECATED use !).
567 567
568 568 DEPRECATED. Suboptimal, retained for backwards compatibility.
569 569
570 570 You should use the form 'var = !command' instead. Example:
571 571
572 572 "%sc -l myfiles = ls ~" should now be written as
573 573
574 574 "myfiles = !ls ~"
575 575
576 576 myfiles.s, myfiles.l and myfiles.n still apply as documented
577 577 below.
578 578
579 579 --
580 580 %sc [options] varname=command
581 581
582 582 IPython will run the given command using commands.getoutput(), and
583 583 will then update the user's interactive namespace with a variable
584 584 called varname, containing the value of the call. Your command can
585 585 contain shell wildcards, pipes, etc.
586 586
587 587 The '=' sign in the syntax is mandatory, and the variable name you
588 588 supply must follow Python's standard conventions for valid names.
589 589
590 590 (A special format without variable name exists for internal use)
591 591
592 592 Options:
593 593
594 594 -l: list output. Split the output on newlines into a list before
595 595 assigning it to the given variable. By default the output is stored
596 596 as a single string.
597 597
598 598 -v: verbose. Print the contents of the variable.
599 599
600 600 In most cases you should not need to split as a list, because the
601 601 returned value is a special type of string which can automatically
602 602 provide its contents either as a list (split on newlines) or as a
603 603 space-separated string. These are convenient, respectively, either
604 604 for sequential processing or to be passed to a shell command.
605 605
606 606 For example::
607 607
608 608 # Capture into variable a
609 609 In [1]: sc a=ls *py
610 610
611 611 # a is a string with embedded newlines
612 612 In [2]: a
613 613 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
614 614
615 615 # which can be seen as a list:
616 616 In [3]: a.l
617 617 Out[3]: ['setup.py', 'win32_manual_post_install.py']
618 618
619 619 # or as a whitespace-separated string:
620 620 In [4]: a.s
621 621 Out[4]: 'setup.py win32_manual_post_install.py'
622 622
623 623 # a.s is useful to pass as a single command line:
624 624 In [5]: !wc -l $a.s
625 625 146 setup.py
626 626 130 win32_manual_post_install.py
627 627 276 total
628 628
629 629 # while the list form is useful to loop over:
630 630 In [6]: for f in a.l:
631 631 ...: !wc -l $f
632 632 ...:
633 633 146 setup.py
634 634 130 win32_manual_post_install.py
635 635
636 636 Similarly, the lists returned by the -l option are also special, in
637 637 the sense that you can equally invoke the .s attribute on them to
638 638 automatically get a whitespace-separated string from their contents::
639 639
640 640 In [7]: sc -l b=ls *py
641 641
642 642 In [8]: b
643 643 Out[8]: ['setup.py', 'win32_manual_post_install.py']
644 644
645 645 In [9]: b.s
646 646 Out[9]: 'setup.py win32_manual_post_install.py'
647 647
648 648 In summary, both the lists and strings used for output capture have
649 649 the following special attributes::
650 650
651 651 .l (or .list) : value as list.
652 652 .n (or .nlstr): value as newline-separated string.
653 653 .s (or .spstr): value as space-separated string.
654 654 """
655 655
656 656 opts,args = self.parse_options(parameter_s, 'lv')
657 657 # Try to get a variable name and command to run
658 658 try:
659 659 # the variable name must be obtained from the parse_options
660 660 # output, which uses shlex.split to strip options out.
661 661 var,_ = args.split('=', 1)
662 662 var = var.strip()
663 663 # But the command has to be extracted from the original input
664 664 # parameter_s, not on what parse_options returns, to avoid the
665 665 # quote stripping which shlex.split performs on it.
666 666 _,cmd = parameter_s.split('=', 1)
667 667 except ValueError:
668 668 var,cmd = '',''
669 669 # If all looks ok, proceed
670 670 split = 'l' in opts
671 671 out = self.shell.getoutput(cmd, split=split)
672 672 if 'v' in opts:
673 673 print('%s ==\n%s' % (var, pformat(out)))
674 674 if var:
675 675 self.shell.user_ns.update({var:out})
676 676 else:
677 677 return out
678 678
679 679 @line_cell_magic
680 680 def sx(self, line='', cell=None):
681 681 """Shell execute - run shell command and capture output (!! is short-hand).
682 682
683 683 %sx command
684 684
685 685 IPython will run the given command using commands.getoutput(), and
686 686 return the result formatted as a list (split on '\\n'). Since the
687 687 output is _returned_, it will be stored in ipython's regular output
688 688 cache Out[N] and in the '_N' automatic variables.
689 689
690 690 Notes:
691 691
692 692 1) If an input line begins with '!!', then %sx is automatically
693 693 invoked. That is, while::
694 694
695 695 !ls
696 696
697 697 causes ipython to simply issue system('ls'), typing::
698 698
699 699 !!ls
700 700
701 701 is a shorthand equivalent to::
702 702
703 703 %sx ls
704 704
705 705 2) %sx differs from %sc in that %sx automatically splits into a list,
706 706 like '%sc -l'. The reason for this is to make it as easy as possible
707 707 to process line-oriented shell output via further python commands.
708 708 %sc is meant to provide much finer control, but requires more
709 709 typing.
710 710
711 711 3) Just like %sc -l, this is a list with special attributes:
712 712 ::
713 713
714 714 .l (or .list) : value as list.
715 715 .n (or .nlstr): value as newline-separated string.
716 716 .s (or .spstr): value as whitespace-separated string.
717 717
718 718 This is very useful when trying to use such lists as arguments to
719 719 system commands."""
720 720
721 721 if cell is None:
722 722 # line magic
723 723 return self.shell.getoutput(line)
724 724 else:
725 725 opts,args = self.parse_options(line, '', 'out=')
726 726 output = self.shell.getoutput(cell)
727 727 out_name = opts.get('out', opts.get('o'))
728 728 if out_name:
729 729 self.shell.user_ns[out_name] = output
730 730 else:
731 731 return output
732 732
733 733 system = line_cell_magic('system')(sx)
734 734 bang = cell_magic('!')(sx)
735 735
736 736 @line_magic
737 737 def bookmark(self, parameter_s=''):
738 738 """Manage IPython's bookmark system.
739 739
740 740 %bookmark <name> - set bookmark to current dir
741 741 %bookmark <name> <dir> - set bookmark to <dir>
742 742 %bookmark -l - list all bookmarks
743 743 %bookmark -d <name> - remove bookmark
744 744 %bookmark -r - remove all bookmarks
745 745
746 746 You can later on access a bookmarked folder with::
747 747
748 748 %cd -b <name>
749 749
750 750 or simply '%cd <name>' if there is no directory called <name> AND
751 751 there is such a bookmark defined.
752 752
753 753 Your bookmarks persist through IPython sessions, but they are
754 754 associated with each profile."""
755 755
756 756 opts,args = self.parse_options(parameter_s,'drl',mode='list')
757 757 if len(args) > 2:
758 758 raise UsageError("%bookmark: too many arguments")
759 759
760 760 bkms = self.shell.db.get('bookmarks',{})
761 761
762 762 if 'd' in opts:
763 763 try:
764 764 todel = args[0]
765 765 except IndexError as e:
766 766 raise UsageError(
767 767 "%bookmark -d: must provide a bookmark to delete") from e
768 768 else:
769 769 try:
770 770 del bkms[todel]
771 771 except KeyError as e:
772 772 raise UsageError(
773 773 "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e
774 774
775 775 elif 'r' in opts:
776 776 bkms = {}
777 777 elif 'l' in opts:
778 778 bks = sorted(bkms)
779 779 if bks:
780 780 size = max(map(len, bks))
781 781 else:
782 782 size = 0
783 783 fmt = '%-'+str(size)+'s -> %s'
784 784 print('Current bookmarks:')
785 785 for bk in bks:
786 786 print(fmt % (bk, bkms[bk]))
787 787 else:
788 788 if not args:
789 789 raise UsageError("%bookmark: You must specify the bookmark name")
790 790 elif len(args)==1:
791 791 bkms[args[0]] = os.getcwd()
792 792 elif len(args)==2:
793 793 bkms[args[0]] = args[1]
794 794 self.shell.db['bookmarks'] = bkms
795 795
796 796 @line_magic
797 797 def pycat(self, parameter_s=''):
798 798 """Show a syntax-highlighted file through a pager.
799 799
800 800 This magic is similar to the cat utility, but it will assume the file
801 801 to be Python source and will show it with syntax highlighting.
802 802
803 803 This magic command can either take a local filename, an url,
804 804 an history range (see %history) or a macro as argument.
805 805
806 806 If no parameter is given, prints out history of current session up to
807 807 this point. ::
808 808
809 809 %pycat myscript.py
810 810 %pycat 7-27
811 811 %pycat myMacro
812 812 %pycat http://www.example.com/myscript.py
813 813 """
814 814 try:
815 815 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
816 816 except (ValueError, IOError):
817 817 print("Error: no such file, variable, URL, history range or macro")
818 818 return
819 819
820 820 page.page(self.shell.pycolorize(source_to_unicode(cont)))
821 821
822 822 @magic_arguments.magic_arguments()
823 823 @magic_arguments.argument(
824 824 '-a', '--append', action='store_true', default=False,
825 825 help='Append contents of the cell to an existing file. '
826 826 'The file will be created if it does not exist.'
827 827 )
828 828 @magic_arguments.argument(
829 829 'filename', type=str,
830 830 help='file to write'
831 831 )
832 832 @cell_magic
833 833 def writefile(self, line, cell):
834 834 """Write the contents of the cell to a file.
835 835
836 836 The file will be overwritten unless the -a (--append) flag is specified.
837 837 """
838 838 args = magic_arguments.parse_argstring(self.writefile, line)
839 839 if re.match(r'^(\'.*\')|(".*")$', args.filename):
840 840 filename = os.path.expanduser(args.filename[1:-1])
841 841 else:
842 842 filename = os.path.expanduser(args.filename)
843 843
844 844 if os.path.exists(filename):
845 845 if args.append:
846 846 print("Appending to %s" % filename)
847 847 else:
848 848 print("Overwriting %s" % filename)
849 849 else:
850 850 print("Writing %s" % filename)
851 851
852 852 mode = 'a' if args.append else 'w'
853 853 with io.open(filename, mode, encoding='utf-8') as f:
854 854 f.write(cell)
General Comments 0
You need to be logged in to leave comments. Login now