##// END OF EJS Templates
MAINT: refactor/please mypy....
Matthias Bussonnier -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,41 +1,38 b''
1 1 name: Run MyPy
2 2
3 3 on:
4 4 push:
5 5 branches: [ main, 7.x]
6 6 pull_request:
7 7 branches: [ main, 7.x]
8 8
9 9 permissions:
10 10 contents: read
11 11
12 12 jobs:
13 13 build:
14 14
15 15 runs-on: ubuntu-latest
16 16 strategy:
17 17 matrix:
18 18 python-version: ["3.x"]
19 19
20 20 steps:
21 21 - uses: actions/checkout@v3
22 22 - name: Set up Python ${{ matrix.python-version }}
23 23 uses: actions/setup-python@v4
24 24 with:
25 25 python-version: ${{ matrix.python-version }}
26 26 - name: Install dependencies
27 27 run: |
28 28 python -m pip install --upgrade pip
29 pip install mypy pyflakes flake8
29 pip install mypy pyflakes flake8 types-decorator
30 30 - name: Lint with mypy
31 31 run: |
32 32 set -e
33 mypy -p IPython.terminal
34 mypy -p IPython.core.magics
35 mypy -p IPython.core.guarded_eval
36 mypy -p IPython.core.completer
33 mypy IPython
37 34 - name: Lint with pyflakes
38 35 run: |
39 36 set -e
40 37 flake8 IPython/core/magics/script.py
41 38 flake8 IPython/core/magics/packaging.py
@@ -1,1026 +1,1028 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Display formatters.
3 3
4 4 Inheritance diagram:
5 5
6 6 .. inheritance-diagram:: IPython.core.formatters
7 7 :parts: 3
8 8 """
9 9
10 10 # Copyright (c) IPython Development Team.
11 11 # Distributed under the terms of the Modified BSD License.
12 12
13 13 import abc
14 14 import sys
15 15 import traceback
16 16 import warnings
17 17 from io import StringIO
18 18
19 19 from decorator import decorator
20 20
21 21 from traitlets.config.configurable import Configurable
22 22 from .getipython import get_ipython
23 23 from ..utils.sentinel import Sentinel
24 24 from ..utils.dir2 import get_real_method
25 25 from ..lib import pretty
26 26 from traitlets import (
27 27 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
28 28 ForwardDeclaredInstance,
29 29 default, observe,
30 30 )
31 31
32 from typing import Any
33
32 34
33 35 class DisplayFormatter(Configurable):
34 36
35 37 active_types = List(Unicode(),
36 38 help="""List of currently active mime-types to display.
37 39 You can use this to set a white-list for formats to display.
38 40
39 41 Most users will not need to change this value.
40 42 """).tag(config=True)
41 43
42 44 @default('active_types')
43 45 def _active_types_default(self):
44 46 return self.format_types
45 47
46 48 @observe('active_types')
47 49 def _active_types_changed(self, change):
48 50 for key, formatter in self.formatters.items():
49 51 if key in change['new']:
50 52 formatter.enabled = True
51 53 else:
52 54 formatter.enabled = False
53 55
54 56 ipython_display_formatter = ForwardDeclaredInstance('FormatterABC')
55 57 @default('ipython_display_formatter')
56 58 def _default_formatter(self):
57 59 return IPythonDisplayFormatter(parent=self)
58 60
59 61 mimebundle_formatter = ForwardDeclaredInstance('FormatterABC')
60 62 @default('mimebundle_formatter')
61 63 def _default_mime_formatter(self):
62 64 return MimeBundleFormatter(parent=self)
63 65
64 66 # A dict of formatter whose keys are format types (MIME types) and whose
65 67 # values are subclasses of BaseFormatter.
66 68 formatters = Dict()
67 69 @default('formatters')
68 70 def _formatters_default(self):
69 71 """Activate the default formatters."""
70 72 formatter_classes = [
71 73 PlainTextFormatter,
72 74 HTMLFormatter,
73 75 MarkdownFormatter,
74 76 SVGFormatter,
75 77 PNGFormatter,
76 78 PDFFormatter,
77 79 JPEGFormatter,
78 80 LatexFormatter,
79 81 JSONFormatter,
80 82 JavascriptFormatter
81 83 ]
82 84 d = {}
83 85 for cls in formatter_classes:
84 86 f = cls(parent=self)
85 87 d[f.format_type] = f
86 88 return d
87 89
88 90 def format(self, obj, include=None, exclude=None):
89 91 """Return a format data dict for an object.
90 92
91 93 By default all format types will be computed.
92 94
93 95 The following MIME types are usually implemented:
94 96
95 97 * text/plain
96 98 * text/html
97 99 * text/markdown
98 100 * text/latex
99 101 * application/json
100 102 * application/javascript
101 103 * application/pdf
102 104 * image/png
103 105 * image/jpeg
104 106 * image/svg+xml
105 107
106 108 Parameters
107 109 ----------
108 110 obj : object
109 111 The Python object whose format data will be computed.
110 112 include : list, tuple or set; optional
111 113 A list of format type strings (MIME types) to include in the
112 114 format data dict. If this is set *only* the format types included
113 115 in this list will be computed.
114 116 exclude : list, tuple or set; optional
115 117 A list of format type string (MIME types) to exclude in the format
116 118 data dict. If this is set all format types will be computed,
117 119 except for those included in this argument.
118 120 Mimetypes present in exclude will take precedence over the ones in include
119 121
120 122 Returns
121 123 -------
122 124 (format_dict, metadata_dict) : tuple of two dicts
123 125 format_dict is a dictionary of key/value pairs, one of each format that was
124 126 generated for the object. The keys are the format types, which
125 127 will usually be MIME type strings and the values and JSON'able
126 128 data structure containing the raw data for the representation in
127 129 that format.
128 130
129 131 metadata_dict is a dictionary of metadata about each mime-type output.
130 132 Its keys will be a strict subset of the keys in format_dict.
131 133
132 134 Notes
133 135 -----
134 136 If an object implement `_repr_mimebundle_` as well as various
135 137 `_repr_*_`, the data returned by `_repr_mimebundle_` will take
136 138 precedence and the corresponding `_repr_*_` for this mimetype will
137 139 not be called.
138 140
139 141 """
140 142 format_dict = {}
141 143 md_dict = {}
142 144
143 145 if self.ipython_display_formatter(obj):
144 146 # object handled itself, don't proceed
145 147 return {}, {}
146 148
147 149 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
148 150
149 151 if format_dict or md_dict:
150 152 if include:
151 153 format_dict = {k:v for k,v in format_dict.items() if k in include}
152 154 md_dict = {k:v for k,v in md_dict.items() if k in include}
153 155 if exclude:
154 156 format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
155 157 md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
156 158
157 159 for format_type, formatter in self.formatters.items():
158 160 if format_type in format_dict:
159 161 # already got it from mimebundle, maybe don't render again.
160 162 # exception: manually registered per-mime renderer
161 163 # check priority:
162 164 # 1. user-registered per-mime formatter
163 165 # 2. mime-bundle (user-registered or repr method)
164 166 # 3. default per-mime formatter (e.g. repr method)
165 167 try:
166 168 formatter.lookup(obj)
167 169 except KeyError:
168 170 # no special formatter, use mime-bundle-provided value
169 171 continue
170 172 if include and format_type not in include:
171 173 continue
172 174 if exclude and format_type in exclude:
173 175 continue
174 176
175 177 md = None
176 178 try:
177 179 data = formatter(obj)
178 180 except:
179 181 # FIXME: log the exception
180 182 raise
181 183
182 184 # formatters can return raw data or (data, metadata)
183 185 if isinstance(data, tuple) and len(data) == 2:
184 186 data, md = data
185 187
186 188 if data is not None:
187 189 format_dict[format_type] = data
188 190 if md is not None:
189 191 md_dict[format_type] = md
190 192 return format_dict, md_dict
191 193
192 194 @property
193 195 def format_types(self):
194 196 """Return the format types (MIME types) of the active formatters."""
195 197 return list(self.formatters.keys())
196 198
197 199
198 200 #-----------------------------------------------------------------------------
199 201 # Formatters for specific format types (text, html, svg, etc.)
200 202 #-----------------------------------------------------------------------------
201 203
202 204
203 205 def _safe_repr(obj):
204 206 """Try to return a repr of an object
205 207
206 208 always returns a string, at least.
207 209 """
208 210 try:
209 211 return repr(obj)
210 212 except Exception as e:
211 213 return "un-repr-able object (%r)" % e
212 214
213 215
214 216 class FormatterWarning(UserWarning):
215 217 """Warning class for errors in formatters"""
216 218
217 219 @decorator
218 220 def catch_format_error(method, self, *args, **kwargs):
219 221 """show traceback on failed format call"""
220 222 try:
221 223 r = method(self, *args, **kwargs)
222 224 except NotImplementedError:
223 225 # don't warn on NotImplementedErrors
224 226 return self._check_return(None, args[0])
225 227 except Exception:
226 228 exc_info = sys.exc_info()
227 229 ip = get_ipython()
228 230 if ip is not None:
229 231 ip.showtraceback(exc_info)
230 232 else:
231 233 traceback.print_exception(*exc_info)
232 234 return self._check_return(None, args[0])
233 235 return self._check_return(r, args[0])
234 236
235 237
236 238 class FormatterABC(metaclass=abc.ABCMeta):
237 239 """ Abstract base class for Formatters.
238 240
239 241 A formatter is a callable class that is responsible for computing the
240 242 raw format data for a particular format type (MIME type). For example,
241 243 an HTML formatter would have a format type of `text/html` and would return
242 244 the HTML representation of the object when called.
243 245 """
244 246
245 247 # The format type of the data returned, usually a MIME type.
246 248 format_type = 'text/plain'
247 249
248 250 # Is the formatter enabled...
249 251 enabled = True
250 252
251 253 @abc.abstractmethod
252 254 def __call__(self, obj):
253 255 """Return a JSON'able representation of the object.
254 256
255 257 If the object cannot be formatted by this formatter,
256 258 warn and return None.
257 259 """
258 260 return repr(obj)
259 261
260 262
261 263 def _mod_name_key(typ):
262 264 """Return a (__module__, __name__) tuple for a type.
263 265
264 266 Used as key in Formatter.deferred_printers.
265 267 """
266 268 module = getattr(typ, '__module__', None)
267 269 name = getattr(typ, '__name__', None)
268 270 return (module, name)
269 271
270 272
271 273 def _get_type(obj):
272 274 """Return the type of an instance (old and new-style)"""
273 275 return getattr(obj, '__class__', None) or type(obj)
274 276
275 277
276 278 _raise_key_error = Sentinel('_raise_key_error', __name__,
277 279 """
278 280 Special value to raise a KeyError
279 281
280 282 Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
281 283 """)
282 284
283 285
284 286 class BaseFormatter(Configurable):
285 287 """A base formatter class that is configurable.
286 288
287 289 This formatter should usually be used as the base class of all formatters.
288 290 It is a traited :class:`Configurable` class and includes an extensible
289 291 API for users to determine how their objects are formatted. The following
290 292 logic is used to find a function to format an given object.
291 293
292 294 1. The object is introspected to see if it has a method with the name
293 295 :attr:`print_method`. If is does, that object is passed to that method
294 296 for formatting.
295 297 2. If no print method is found, three internal dictionaries are consulted
296 298 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
297 299 and :attr:`deferred_printers`.
298 300
299 301 Users should use these dictionaries to register functions that will be
300 302 used to compute the format data for their objects (if those objects don't
301 303 have the special print methods). The easiest way of using these
302 304 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
303 305 methods.
304 306
305 307 If no function/callable is found to compute the format data, ``None`` is
306 308 returned and this format type is not used.
307 309 """
308 310
309 format_type = Unicode('text/plain')
310 _return_type = str
311 format_type = Unicode("text/plain")
312 _return_type: Any = str
311 313
312 314 enabled = Bool(True).tag(config=True)
313 315
314 316 print_method = ObjectName('__repr__')
315 317
316 318 # The singleton printers.
317 319 # Maps the IDs of the builtin singleton objects to the format functions.
318 320 singleton_printers = Dict().tag(config=True)
319 321
320 322 # The type-specific printers.
321 323 # Map type objects to the format functions.
322 324 type_printers = Dict().tag(config=True)
323 325
324 326 # The deferred-import type-specific printers.
325 327 # Map (modulename, classname) pairs to the format functions.
326 328 deferred_printers = Dict().tag(config=True)
327 329
328 330 @catch_format_error
329 331 def __call__(self, obj):
330 332 """Compute the format for an object."""
331 333 if self.enabled:
332 334 # lookup registered printer
333 335 try:
334 336 printer = self.lookup(obj)
335 337 except KeyError:
336 338 pass
337 339 else:
338 340 return printer(obj)
339 341 # Finally look for special method names
340 342 method = get_real_method(obj, self.print_method)
341 343 if method is not None:
342 344 return method()
343 345 return None
344 346 else:
345 347 return None
346 348
347 349 def __contains__(self, typ):
348 350 """map in to lookup_by_type"""
349 351 try:
350 352 self.lookup_by_type(typ)
351 353 except KeyError:
352 354 return False
353 355 else:
354 356 return True
355 357
356 358 def _check_return(self, r, obj):
357 359 """Check that a return value is appropriate
358 360
359 361 Return the value if so, None otherwise, warning if invalid.
360 362 """
361 363 if r is None or isinstance(r, self._return_type) or \
362 364 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
363 365 return r
364 366 else:
365 367 warnings.warn(
366 368 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
367 369 (self.format_type, type(r), self._return_type, _safe_repr(obj)),
368 370 FormatterWarning
369 371 )
370 372
371 373 def lookup(self, obj):
372 374 """Look up the formatter for a given instance.
373 375
374 376 Parameters
375 377 ----------
376 378 obj : object instance
377 379
378 380 Returns
379 381 -------
380 382 f : callable
381 383 The registered formatting callable for the type.
382 384
383 385 Raises
384 386 ------
385 387 KeyError if the type has not been registered.
386 388 """
387 389 # look for singleton first
388 390 obj_id = id(obj)
389 391 if obj_id in self.singleton_printers:
390 392 return self.singleton_printers[obj_id]
391 393 # then lookup by type
392 394 return self.lookup_by_type(_get_type(obj))
393 395
394 396 def lookup_by_type(self, typ):
395 397 """Look up the registered formatter for a type.
396 398
397 399 Parameters
398 400 ----------
399 401 typ : type or '__module__.__name__' string for a type
400 402
401 403 Returns
402 404 -------
403 405 f : callable
404 406 The registered formatting callable for the type.
405 407
406 408 Raises
407 409 ------
408 410 KeyError if the type has not been registered.
409 411 """
410 412 if isinstance(typ, str):
411 413 typ_key = tuple(typ.rsplit('.',1))
412 414 if typ_key not in self.deferred_printers:
413 415 # We may have it cached in the type map. We will have to
414 416 # iterate over all of the types to check.
415 417 for cls in self.type_printers:
416 418 if _mod_name_key(cls) == typ_key:
417 419 return self.type_printers[cls]
418 420 else:
419 421 return self.deferred_printers[typ_key]
420 422 else:
421 423 for cls in pretty._get_mro(typ):
422 424 if cls in self.type_printers or self._in_deferred_types(cls):
423 425 return self.type_printers[cls]
424 426
425 427 # If we have reached here, the lookup failed.
426 428 raise KeyError("No registered printer for {0!r}".format(typ))
427 429
428 430 def for_type(self, typ, func=None):
429 431 """Add a format function for a given type.
430 432
431 433 Parameters
432 434 ----------
433 435 typ : type or '__module__.__name__' string for a type
434 436 The class of the object that will be formatted using `func`.
435 437
436 438 func : callable
437 439 A callable for computing the format data.
438 440 `func` will be called with the object to be formatted,
439 441 and will return the raw data in this formatter's format.
440 442 Subclasses may use a different call signature for the
441 443 `func` argument.
442 444
443 445 If `func` is None or not specified, there will be no change,
444 446 only returning the current value.
445 447
446 448 Returns
447 449 -------
448 450 oldfunc : callable
449 451 The currently registered callable.
450 452 If you are registering a new formatter,
451 453 this will be the previous value (to enable restoring later).
452 454 """
453 455 # if string given, interpret as 'pkg.module.class_name'
454 456 if isinstance(typ, str):
455 457 type_module, type_name = typ.rsplit('.', 1)
456 458 return self.for_type_by_name(type_module, type_name, func)
457 459
458 460 try:
459 461 oldfunc = self.lookup_by_type(typ)
460 462 except KeyError:
461 463 oldfunc = None
462 464
463 465 if func is not None:
464 466 self.type_printers[typ] = func
465 467
466 468 return oldfunc
467 469
468 470 def for_type_by_name(self, type_module, type_name, func=None):
469 471 """Add a format function for a type specified by the full dotted
470 472 module and name of the type, rather than the type of the object.
471 473
472 474 Parameters
473 475 ----------
474 476 type_module : str
475 477 The full dotted name of the module the type is defined in, like
476 478 ``numpy``.
477 479
478 480 type_name : str
479 481 The name of the type (the class name), like ``dtype``
480 482
481 483 func : callable
482 484 A callable for computing the format data.
483 485 `func` will be called with the object to be formatted,
484 486 and will return the raw data in this formatter's format.
485 487 Subclasses may use a different call signature for the
486 488 `func` argument.
487 489
488 490 If `func` is None or unspecified, there will be no change,
489 491 only returning the current value.
490 492
491 493 Returns
492 494 -------
493 495 oldfunc : callable
494 496 The currently registered callable.
495 497 If you are registering a new formatter,
496 498 this will be the previous value (to enable restoring later).
497 499 """
498 500 key = (type_module, type_name)
499 501
500 502 try:
501 503 oldfunc = self.lookup_by_type("%s.%s" % key)
502 504 except KeyError:
503 505 oldfunc = None
504 506
505 507 if func is not None:
506 508 self.deferred_printers[key] = func
507 509 return oldfunc
508 510
509 511 def pop(self, typ, default=_raise_key_error):
510 512 """Pop a formatter for the given type.
511 513
512 514 Parameters
513 515 ----------
514 516 typ : type or '__module__.__name__' string for a type
515 517 default : object
516 518 value to be returned if no formatter is registered for typ.
517 519
518 520 Returns
519 521 -------
520 522 obj : object
521 523 The last registered object for the type.
522 524
523 525 Raises
524 526 ------
525 527 KeyError if the type is not registered and default is not specified.
526 528 """
527 529
528 530 if isinstance(typ, str):
529 531 typ_key = tuple(typ.rsplit('.',1))
530 532 if typ_key not in self.deferred_printers:
531 533 # We may have it cached in the type map. We will have to
532 534 # iterate over all of the types to check.
533 535 for cls in self.type_printers:
534 536 if _mod_name_key(cls) == typ_key:
535 537 old = self.type_printers.pop(cls)
536 538 break
537 539 else:
538 540 old = default
539 541 else:
540 542 old = self.deferred_printers.pop(typ_key)
541 543 else:
542 544 if typ in self.type_printers:
543 545 old = self.type_printers.pop(typ)
544 546 else:
545 547 old = self.deferred_printers.pop(_mod_name_key(typ), default)
546 548 if old is _raise_key_error:
547 549 raise KeyError("No registered value for {0!r}".format(typ))
548 550 return old
549 551
550 552 def _in_deferred_types(self, cls):
551 553 """
552 554 Check if the given class is specified in the deferred type registry.
553 555
554 556 Successful matches will be moved to the regular type registry for future use.
555 557 """
556 558 mod = getattr(cls, '__module__', None)
557 559 name = getattr(cls, '__name__', None)
558 560 key = (mod, name)
559 561 if key in self.deferred_printers:
560 562 # Move the printer over to the regular registry.
561 563 printer = self.deferred_printers.pop(key)
562 564 self.type_printers[cls] = printer
563 565 return True
564 566 return False
565 567
566 568
567 569 class PlainTextFormatter(BaseFormatter):
568 570 """The default pretty-printer.
569 571
570 572 This uses :mod:`IPython.lib.pretty` to compute the format data of
571 573 the object. If the object cannot be pretty printed, :func:`repr` is used.
572 574 See the documentation of :mod:`IPython.lib.pretty` for details on
573 575 how to write pretty printers. Here is a simple example::
574 576
575 577 def dtype_pprinter(obj, p, cycle):
576 578 if cycle:
577 579 return p.text('dtype(...)')
578 580 if hasattr(obj, 'fields'):
579 581 if obj.fields is None:
580 582 p.text(repr(obj))
581 583 else:
582 584 p.begin_group(7, 'dtype([')
583 585 for i, field in enumerate(obj.descr):
584 586 if i > 0:
585 587 p.text(',')
586 588 p.breakable()
587 589 p.pretty(field)
588 590 p.end_group(7, '])')
589 591 """
590 592
591 593 # The format type of data returned.
592 594 format_type = Unicode('text/plain')
593 595
594 596 # This subclass ignores this attribute as it always need to return
595 597 # something.
596 598 enabled = Bool(True).tag(config=False)
597 599
598 600 max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
599 601 help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
600 602
601 603 Set to 0 to disable truncation.
602 604 """
603 605 ).tag(config=True)
604 606
605 607 # Look for a _repr_pretty_ methods to use for pretty printing.
606 608 print_method = ObjectName('_repr_pretty_')
607 609
608 610 # Whether to pretty-print or not.
609 611 pprint = Bool(True).tag(config=True)
610 612
611 613 # Whether to be verbose or not.
612 614 verbose = Bool(False).tag(config=True)
613 615
614 616 # The maximum width.
615 617 max_width = Integer(79).tag(config=True)
616 618
617 619 # The newline character.
618 620 newline = Unicode('\n').tag(config=True)
619 621
620 622 # format-string for pprinting floats
621 623 float_format = Unicode('%r')
622 624 # setter for float precision, either int or direct format-string
623 625 float_precision = CUnicode('').tag(config=True)
624 626
625 627 @observe('float_precision')
626 628 def _float_precision_changed(self, change):
627 629 """float_precision changed, set float_format accordingly.
628 630
629 631 float_precision can be set by int or str.
630 632 This will set float_format, after interpreting input.
631 633 If numpy has been imported, numpy print precision will also be set.
632 634
633 635 integer `n` sets format to '%.nf', otherwise, format set directly.
634 636
635 637 An empty string returns to defaults (repr for float, 8 for numpy).
636 638
637 639 This parameter can be set via the '%precision' magic.
638 640 """
639 641 new = change['new']
640 642 if '%' in new:
641 643 # got explicit format string
642 644 fmt = new
643 645 try:
644 646 fmt%3.14159
645 647 except Exception as e:
646 648 raise ValueError("Precision must be int or format string, not %r"%new) from e
647 649 elif new:
648 650 # otherwise, should be an int
649 651 try:
650 652 i = int(new)
651 653 assert i >= 0
652 654 except ValueError as e:
653 655 raise ValueError("Precision must be int or format string, not %r"%new) from e
654 656 except AssertionError as e:
655 657 raise ValueError("int precision must be non-negative, not %r"%i) from e
656 658
657 659 fmt = '%%.%if'%i
658 660 if 'numpy' in sys.modules:
659 661 # set numpy precision if it has been imported
660 662 import numpy
661 663 numpy.set_printoptions(precision=i)
662 664 else:
663 665 # default back to repr
664 666 fmt = '%r'
665 667 if 'numpy' in sys.modules:
666 668 import numpy
667 669 # numpy default is 8
668 670 numpy.set_printoptions(precision=8)
669 671 self.float_format = fmt
670 672
671 673 # Use the default pretty printers from IPython.lib.pretty.
672 674 @default('singleton_printers')
673 675 def _singleton_printers_default(self):
674 676 return pretty._singleton_pprinters.copy()
675 677
676 678 @default('type_printers')
677 679 def _type_printers_default(self):
678 680 d = pretty._type_pprinters.copy()
679 681 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
680 682 # if NumPy is used, set precision for its float64 type
681 683 if "numpy" in sys.modules:
682 684 import numpy
683 685
684 686 d[numpy.float64] = lambda obj, p, cycle: p.text(self.float_format % obj)
685 687 return d
686 688
687 689 @default('deferred_printers')
688 690 def _deferred_printers_default(self):
689 691 return pretty._deferred_type_pprinters.copy()
690 692
691 693 #### FormatterABC interface ####
692 694
693 695 @catch_format_error
694 696 def __call__(self, obj):
695 697 """Compute the pretty representation of the object."""
696 698 if not self.pprint:
697 699 return repr(obj)
698 700 else:
699 701 stream = StringIO()
700 702 printer = pretty.RepresentationPrinter(stream, self.verbose,
701 703 self.max_width, self.newline,
702 704 max_seq_length=self.max_seq_length,
703 705 singleton_pprinters=self.singleton_printers,
704 706 type_pprinters=self.type_printers,
705 707 deferred_pprinters=self.deferred_printers)
706 708 printer.pretty(obj)
707 709 printer.flush()
708 710 return stream.getvalue()
709 711
710 712
711 713 class HTMLFormatter(BaseFormatter):
712 714 """An HTML formatter.
713 715
714 716 To define the callables that compute the HTML representation of your
715 717 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
716 718 or :meth:`for_type_by_name` methods to register functions that handle
717 719 this.
718 720
719 721 The return value of this formatter should be a valid HTML snippet that
720 722 could be injected into an existing DOM. It should *not* include the
721 723 ```<html>`` or ```<body>`` tags.
722 724 """
723 725 format_type = Unicode('text/html')
724 726
725 727 print_method = ObjectName('_repr_html_')
726 728
727 729
728 730 class MarkdownFormatter(BaseFormatter):
729 731 """A Markdown formatter.
730 732
731 733 To define the callables that compute the Markdown representation of your
732 734 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
733 735 or :meth:`for_type_by_name` methods to register functions that handle
734 736 this.
735 737
736 738 The return value of this formatter should be a valid Markdown.
737 739 """
738 740 format_type = Unicode('text/markdown')
739 741
740 742 print_method = ObjectName('_repr_markdown_')
741 743
742 744 class SVGFormatter(BaseFormatter):
743 745 """An SVG formatter.
744 746
745 747 To define the callables that compute the SVG representation of your
746 748 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
747 749 or :meth:`for_type_by_name` methods to register functions that handle
748 750 this.
749 751
750 752 The return value of this formatter should be valid SVG enclosed in
751 753 ```<svg>``` tags, that could be injected into an existing DOM. It should
752 754 *not* include the ```<html>`` or ```<body>`` tags.
753 755 """
754 756 format_type = Unicode('image/svg+xml')
755 757
756 758 print_method = ObjectName('_repr_svg_')
757 759
758 760
759 761 class PNGFormatter(BaseFormatter):
760 762 """A PNG formatter.
761 763
762 764 To define the callables that compute the PNG representation of your
763 765 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
764 766 or :meth:`for_type_by_name` methods to register functions that handle
765 767 this.
766 768
767 769 The return value of this formatter should be raw PNG data, *not*
768 770 base64 encoded.
769 771 """
770 772 format_type = Unicode('image/png')
771 773
772 774 print_method = ObjectName('_repr_png_')
773 775
774 776 _return_type = (bytes, str)
775 777
776 778
777 779 class JPEGFormatter(BaseFormatter):
778 780 """A JPEG formatter.
779 781
780 782 To define the callables that compute the JPEG representation of your
781 783 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
782 784 or :meth:`for_type_by_name` methods to register functions that handle
783 785 this.
784 786
785 787 The return value of this formatter should be raw JPEG data, *not*
786 788 base64 encoded.
787 789 """
788 790 format_type = Unicode('image/jpeg')
789 791
790 792 print_method = ObjectName('_repr_jpeg_')
791 793
792 794 _return_type = (bytes, str)
793 795
794 796
795 797 class LatexFormatter(BaseFormatter):
796 798 """A LaTeX formatter.
797 799
798 800 To define the callables that compute the LaTeX representation of your
799 801 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
800 802 or :meth:`for_type_by_name` methods to register functions that handle
801 803 this.
802 804
803 805 The return value of this formatter should be a valid LaTeX equation,
804 806 enclosed in either ```$```, ```$$``` or another LaTeX equation
805 807 environment.
806 808 """
807 809 format_type = Unicode('text/latex')
808 810
809 811 print_method = ObjectName('_repr_latex_')
810 812
811 813
812 814 class JSONFormatter(BaseFormatter):
813 815 """A JSON string formatter.
814 816
815 817 To define the callables that compute the JSONable representation of
816 818 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
817 819 or :meth:`for_type_by_name` methods to register functions that handle
818 820 this.
819 821
820 822 The return value of this formatter should be a JSONable list or dict.
821 823 JSON scalars (None, number, string) are not allowed, only dict or list containers.
822 824 """
823 825 format_type = Unicode('application/json')
824 826 _return_type = (list, dict)
825 827
826 828 print_method = ObjectName('_repr_json_')
827 829
828 830 def _check_return(self, r, obj):
829 831 """Check that a return value is appropriate
830 832
831 833 Return the value if so, None otherwise, warning if invalid.
832 834 """
833 835 if r is None:
834 836 return
835 837 md = None
836 838 if isinstance(r, tuple):
837 839 # unpack data, metadata tuple for type checking on first element
838 840 r, md = r
839 841
840 842 assert not isinstance(
841 843 r, str
842 844 ), "JSON-as-string has been deprecated since IPython < 3"
843 845
844 846 if md is not None:
845 847 # put the tuple back together
846 848 r = (r, md)
847 849 return super(JSONFormatter, self)._check_return(r, obj)
848 850
849 851
850 852 class JavascriptFormatter(BaseFormatter):
851 853 """A Javascript formatter.
852 854
853 855 To define the callables that compute the Javascript representation of
854 856 your objects, define a :meth:`_repr_javascript_` method or use the
855 857 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
856 858 that handle this.
857 859
858 860 The return value of this formatter should be valid Javascript code and
859 861 should *not* be enclosed in ```<script>``` tags.
860 862 """
861 863 format_type = Unicode('application/javascript')
862 864
863 865 print_method = ObjectName('_repr_javascript_')
864 866
865 867
866 868 class PDFFormatter(BaseFormatter):
867 869 """A PDF formatter.
868 870
869 871 To define the callables that compute the PDF representation of your
870 872 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
871 873 or :meth:`for_type_by_name` methods to register functions that handle
872 874 this.
873 875
874 876 The return value of this formatter should be raw PDF data, *not*
875 877 base64 encoded.
876 878 """
877 879 format_type = Unicode('application/pdf')
878 880
879 881 print_method = ObjectName('_repr_pdf_')
880 882
881 883 _return_type = (bytes, str)
882 884
883 885 class IPythonDisplayFormatter(BaseFormatter):
884 886 """An escape-hatch Formatter for objects that know how to display themselves.
885 887
886 888 To define the callables that compute the representation of your
887 889 objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
888 890 or :meth:`for_type_by_name` methods to register functions that handle
889 891 this. Unlike mime-type displays, this method should not return anything,
890 892 instead calling any appropriate display methods itself.
891 893
892 894 This display formatter has highest priority.
893 895 If it fires, no other display formatter will be called.
894 896
895 897 Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
896 898 without registering a new Formatter.
897 899
898 900 IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
899 901 so `_ipython_display_` should only be used for objects that require unusual
900 902 display patterns, such as multiple display calls.
901 903 """
902 904 print_method = ObjectName('_ipython_display_')
903 905 _return_type = (type(None), bool)
904 906
905 907 @catch_format_error
906 908 def __call__(self, obj):
907 909 """Compute the format for an object."""
908 910 if self.enabled:
909 911 # lookup registered printer
910 912 try:
911 913 printer = self.lookup(obj)
912 914 except KeyError:
913 915 pass
914 916 else:
915 917 printer(obj)
916 918 return True
917 919 # Finally look for special method names
918 920 method = get_real_method(obj, self.print_method)
919 921 if method is not None:
920 922 method()
921 923 return True
922 924
923 925
924 926 class MimeBundleFormatter(BaseFormatter):
925 927 """A Formatter for arbitrary mime-types.
926 928
927 929 Unlike other `_repr_<mimetype>_` methods,
928 930 `_repr_mimebundle_` should return mime-bundle data,
929 931 either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
930 932 Any mime-type is valid.
931 933
932 934 To define the callables that compute the mime-bundle representation of your
933 935 objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
934 936 or :meth:`for_type_by_name` methods to register functions that handle
935 937 this.
936 938
937 939 .. versionadded:: 6.1
938 940 """
939 941 print_method = ObjectName('_repr_mimebundle_')
940 942 _return_type = dict
941 943
942 944 def _check_return(self, r, obj):
943 945 r = super(MimeBundleFormatter, self)._check_return(r, obj)
944 946 # always return (data, metadata):
945 947 if r is None:
946 948 return {}, {}
947 949 if not isinstance(r, tuple):
948 950 return r, {}
949 951 return r
950 952
951 953 @catch_format_error
952 954 def __call__(self, obj, include=None, exclude=None):
953 955 """Compute the format for an object.
954 956
955 957 Identical to parent's method but we pass extra parameters to the method.
956 958
957 959 Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
958 960 particular `include` and `exclude`.
959 961 """
960 962 if self.enabled:
961 963 # lookup registered printer
962 964 try:
963 965 printer = self.lookup(obj)
964 966 except KeyError:
965 967 pass
966 968 else:
967 969 return printer(obj)
968 970 # Finally look for special method names
969 971 method = get_real_method(obj, self.print_method)
970 972
971 973 if method is not None:
972 974 return method(include=include, exclude=exclude)
973 975 return None
974 976 else:
975 977 return None
976 978
977 979
978 980 FormatterABC.register(BaseFormatter)
979 981 FormatterABC.register(PlainTextFormatter)
980 982 FormatterABC.register(HTMLFormatter)
981 983 FormatterABC.register(MarkdownFormatter)
982 984 FormatterABC.register(SVGFormatter)
983 985 FormatterABC.register(PNGFormatter)
984 986 FormatterABC.register(PDFFormatter)
985 987 FormatterABC.register(JPEGFormatter)
986 988 FormatterABC.register(LatexFormatter)
987 989 FormatterABC.register(JSONFormatter)
988 990 FormatterABC.register(JavascriptFormatter)
989 991 FormatterABC.register(IPythonDisplayFormatter)
990 992 FormatterABC.register(MimeBundleFormatter)
991 993
992 994
993 995 def format_display_data(obj, include=None, exclude=None):
994 996 """Return a format data dict for an object.
995 997
996 998 By default all format types will be computed.
997 999
998 1000 Parameters
999 1001 ----------
1000 1002 obj : object
1001 1003 The Python object whose format data will be computed.
1002 1004
1003 1005 Returns
1004 1006 -------
1005 1007 format_dict : dict
1006 1008 A dictionary of key/value pairs, one or each format that was
1007 1009 generated for the object. The keys are the format types, which
1008 1010 will usually be MIME type strings and the values and JSON'able
1009 1011 data structure containing the raw data for the representation in
1010 1012 that format.
1011 1013 include : list or tuple, optional
1012 1014 A list of format type strings (MIME types) to include in the
1013 1015 format data dict. If this is set *only* the format types included
1014 1016 in this list will be computed.
1015 1017 exclude : list or tuple, optional
1016 1018 A list of format type string (MIME types) to exclude in the format
1017 1019 data dict. If this is set all format types will be computed,
1018 1020 except for those included in this argument.
1019 1021 """
1020 1022 from .interactiveshell import InteractiveShell
1021 1023
1022 1024 return InteractiveShell.instance().display_formatter.format(
1023 1025 obj,
1024 1026 include,
1025 1027 exclude
1026 1028 )
@@ -1,772 +1,773 b''
1 1 """DEPRECATED: Input handling and transformation machinery.
2 2
3 3 This module was deprecated in IPython 7.0, in favour of inputtransformer2.
4 4
5 5 The first class in this module, :class:`InputSplitter`, is designed to tell when
6 6 input from a line-oriented frontend is complete and should be executed, and when
7 7 the user should be prompted for another line of code instead. The name 'input
8 8 splitter' is largely for historical reasons.
9 9
10 10 A companion, :class:`IPythonInputSplitter`, provides the same functionality but
11 11 with full support for the extended IPython syntax (magics, system calls, etc).
12 12 The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`.
13 13 :class:`IPythonInputSplitter` feeds the raw code to the transformers in order
14 14 and stores the results.
15 15
16 16 For more details, see the class docstrings below.
17 17 """
18 18
19 19 from warnings import warn
20 20
21 21 warn('IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`',
22 22 DeprecationWarning)
23 23
24 24 # Copyright (c) IPython Development Team.
25 25 # Distributed under the terms of the Modified BSD License.
26 26 import ast
27 27 import codeop
28 28 import io
29 29 import re
30 30 import sys
31 31 import tokenize
32 32 import warnings
33 33
34 from typing import List
35
34 36 from IPython.core.inputtransformer import (leading_indent,
35 37 classic_prompt,
36 38 ipy_prompt,
37 39 cellmagic,
38 40 assemble_logical_lines,
39 41 help_end,
40 42 escaped_commands,
41 43 assign_from_magic,
42 44 assign_from_system,
43 45 assemble_python_lines,
44 46 )
45 47
46 48 # These are available in this module for backwards compatibility.
47 49 from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
48 50 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,
49 51 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES)
50 52
51 53 #-----------------------------------------------------------------------------
52 54 # Utilities
53 55 #-----------------------------------------------------------------------------
54 56
55 57 # FIXME: These are general-purpose utilities that later can be moved to the
56 58 # general ward. Kept here for now because we're being very strict about test
57 59 # coverage with this code, and this lets us ensure that we keep 100% coverage
58 60 # while developing.
59 61
60 62 # compiled regexps for autoindent management
61 63 dedent_re = re.compile('|'.join([
62 64 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe)
63 65 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren
64 66 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe)
65 67 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren
66 68 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces)
67 69 r'^\s+break\s*$', # break (optionally followed by trailing spaces)
68 70 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces)
69 71 ]))
70 72 ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)')
71 73
72 74 # regexp to match pure comment lines so we don't accidentally insert 'if 1:'
73 75 # before pure comments
74 76 comment_line_re = re.compile(r'^\s*\#')
75 77
76 78
77 79 def num_ini_spaces(s):
78 80 """Return the number of initial spaces in a string.
79 81
80 82 Note that tabs are counted as a single space. For now, we do *not* support
81 83 mixing of tabs and spaces in the user's input.
82 84
83 85 Parameters
84 86 ----------
85 87 s : string
86 88
87 89 Returns
88 90 -------
89 91 n : int
90 92 """
91 93
92 94 ini_spaces = ini_spaces_re.match(s)
93 95 if ini_spaces:
94 96 return ini_spaces.end()
95 97 else:
96 98 return 0
97 99
98 100 # Fake token types for partial_tokenize:
99 101 INCOMPLETE_STRING = tokenize.N_TOKENS
100 102 IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1
101 103
102 104 # The 2 classes below have the same API as TokenInfo, but don't try to look up
103 105 # a token type name that they won't find.
104 106 class IncompleteString:
105 107 type = exact_type = INCOMPLETE_STRING
106 108 def __init__(self, s, start, end, line):
107 109 self.s = s
108 110 self.start = start
109 111 self.end = end
110 112 self.line = line
111 113
112 114 class InMultilineStatement:
113 115 type = exact_type = IN_MULTILINE_STATEMENT
114 116 def __init__(self, pos, line):
115 117 self.s = ''
116 118 self.start = self.end = pos
117 119 self.line = line
118 120
119 121 def partial_tokens(s):
120 122 """Iterate over tokens from a possibly-incomplete string of code.
121 123
122 124 This adds two special token types: INCOMPLETE_STRING and
123 125 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and
124 126 represent the two main ways for code to be incomplete.
125 127 """
126 128 readline = io.StringIO(s).readline
127 129 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
128 130 try:
129 131 for token in tokenize.generate_tokens(readline):
130 132 yield token
131 133 except tokenize.TokenError as e:
132 134 # catch EOF error
133 135 lines = s.splitlines(keepends=True)
134 136 end = len(lines), len(lines[-1])
135 137 if 'multi-line string' in e.args[0]:
136 138 l, c = start = token.end
137 139 s = lines[l-1][c:] + ''.join(lines[l:])
138 140 yield IncompleteString(s, start, end, lines[-1])
139 141 elif 'multi-line statement' in e.args[0]:
140 142 yield InMultilineStatement(end, lines[-1])
141 143 else:
142 144 raise
143 145
144 146 def find_next_indent(code):
145 147 """Find the number of spaces for the next line of indentation"""
146 148 tokens = list(partial_tokens(code))
147 149 if tokens[-1].type == tokenize.ENDMARKER:
148 150 tokens.pop()
149 151 if not tokens:
150 152 return 0
151 153 while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
152 154 tokens.pop()
153 155
154 156 if tokens[-1].type == INCOMPLETE_STRING:
155 157 # Inside a multiline string
156 158 return 0
157 159
158 160 # Find the indents used before
159 161 prev_indents = [0]
160 162 def _add_indent(n):
161 163 if n != prev_indents[-1]:
162 164 prev_indents.append(n)
163 165
164 166 tokiter = iter(tokens)
165 167 for tok in tokiter:
166 168 if tok.type in {tokenize.INDENT, tokenize.DEDENT}:
167 169 _add_indent(tok.end[1])
168 170 elif (tok.type == tokenize.NL):
169 171 try:
170 172 _add_indent(next(tokiter).start[1])
171 173 except StopIteration:
172 174 break
173 175
174 176 last_indent = prev_indents.pop()
175 177
176 178 # If we've just opened a multiline statement (e.g. 'a = ['), indent more
177 179 if tokens[-1].type == IN_MULTILINE_STATEMENT:
178 180 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}:
179 181 return last_indent + 4
180 182 return last_indent
181 183
182 184 if tokens[-1].exact_type == tokenize.COLON:
183 185 # Line ends with colon - indent
184 186 return last_indent + 4
185 187
186 188 if last_indent:
187 189 # Examine the last line for dedent cues - statements like return or
188 190 # raise which normally end a block of code.
189 191 last_line_starts = 0
190 192 for i, tok in enumerate(tokens):
191 193 if tok.type == tokenize.NEWLINE:
192 194 last_line_starts = i + 1
193 195
194 196 last_line_tokens = tokens[last_line_starts:]
195 197 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME]
196 198 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}:
197 199 # Find the most recent indentation less than the current level
198 200 for indent in reversed(prev_indents):
199 201 if indent < last_indent:
200 202 return indent
201 203
202 204 return last_indent
203 205
204 206
205 207 def last_blank(src):
206 208 """Determine if the input source ends in a blank.
207 209
208 210 A blank is either a newline or a line consisting of whitespace.
209 211
210 212 Parameters
211 213 ----------
212 214 src : string
213 215 A single or multiline string.
214 216 """
215 217 if not src: return False
216 218 ll = src.splitlines()[-1]
217 219 return (ll == '') or ll.isspace()
218 220
219 221
220 222 last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE)
221 223 last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE)
222 224
223 225 def last_two_blanks(src):
224 226 """Determine if the input source ends in two blanks.
225 227
226 228 A blank is either a newline or a line consisting of whitespace.
227 229
228 230 Parameters
229 231 ----------
230 232 src : string
231 233 A single or multiline string.
232 234 """
233 235 if not src: return False
234 236 # The logic here is tricky: I couldn't get a regexp to work and pass all
235 237 # the tests, so I took a different approach: split the source by lines,
236 238 # grab the last two and prepend '###\n' as a stand-in for whatever was in
237 239 # the body before the last two lines. Then, with that structure, it's
238 240 # possible to analyze with two regexps. Not the most elegant solution, but
239 241 # it works. If anyone tries to change this logic, make sure to validate
240 242 # the whole test suite first!
241 243 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:])
242 244 return (bool(last_two_blanks_re.match(new_src)) or
243 245 bool(last_two_blanks_re2.match(new_src)) )
244 246
245 247
246 248 def remove_comments(src):
247 249 """Remove all comments from input source.
248 250
249 251 Note: comments are NOT recognized inside of strings!
250 252
251 253 Parameters
252 254 ----------
253 255 src : string
254 256 A single or multiline input string.
255 257
256 258 Returns
257 259 -------
258 260 String with all Python comments removed.
259 261 """
260 262
261 263 return re.sub('#.*', '', src)
262 264
263 265
264 266 def get_input_encoding():
265 267 """Return the default standard input encoding.
266 268
267 269 If sys.stdin has no encoding, 'ascii' is returned."""
268 270 # There are strange environments for which sys.stdin.encoding is None. We
269 271 # ensure that a valid encoding is returned.
270 272 encoding = getattr(sys.stdin, 'encoding', None)
271 273 if encoding is None:
272 274 encoding = 'ascii'
273 275 return encoding
274 276
275 277 #-----------------------------------------------------------------------------
276 278 # Classes and functions for normal Python syntax handling
277 279 #-----------------------------------------------------------------------------
278 280
279 281 class InputSplitter(object):
280 282 r"""An object that can accumulate lines of Python source before execution.
281 283
282 284 This object is designed to be fed python source line-by-line, using
283 285 :meth:`push`. It will return on each push whether the currently pushed
284 286 code could be executed already. In addition, it provides a method called
285 287 :meth:`push_accepts_more` that can be used to query whether more input
286 288 can be pushed into a single interactive block.
287 289
288 290 This is a simple example of how an interactive terminal-based client can use
289 291 this tool::
290 292
291 293 isp = InputSplitter()
292 294 while isp.push_accepts_more():
293 295 indent = ' '*isp.indent_spaces
294 296 prompt = '>>> ' + indent
295 297 line = indent + raw_input(prompt)
296 298 isp.push(line)
297 299 print 'Input source was:\n', isp.source_reset(),
298 300 """
299 301 # A cache for storing the current indentation
300 302 # The first value stores the most recently processed source input
301 303 # The second value is the number of spaces for the current indentation
302 304 # If self.source matches the first value, the second value is a valid
303 305 # current indentation. Otherwise, the cache is invalid and the indentation
304 306 # must be recalculated.
305 307 _indent_spaces_cache = None, None
306 308 # String, indicating the default input encoding. It is computed by default
307 309 # at initialization time via get_input_encoding(), but it can be reset by a
308 310 # client with specific knowledge of the encoding.
309 311 encoding = ''
310 312 # String where the current full source input is stored, properly encoded.
311 313 # Reading this attribute is the normal way of querying the currently pushed
312 314 # source code, that has been properly encoded.
313 315 source = ''
314 316 # Code object corresponding to the current source. It is automatically
315 317 # synced to the source, so it can be queried at any time to obtain the code
316 318 # object; it will be None if the source doesn't compile to valid Python.
317 319 code = None
318 320
319 321 # Private attributes
320 322
321 323 # List with lines of input accumulated so far
322 _buffer = None
324 _buffer: List[str]
323 325 # Command compiler
324 _compile = None
326 _compile: codeop.CommandCompiler
325 327 # Boolean indicating whether the current block is complete
326 328 _is_complete = None
327 329 # Boolean indicating whether the current block has an unrecoverable syntax error
328 330 _is_invalid = False
329 331
330 def __init__(self):
331 """Create a new InputSplitter instance.
332 """
332 def __init__(self) -> None:
333 """Create a new InputSplitter instance."""
333 334 self._buffer = []
334 335 self._compile = codeop.CommandCompiler()
335 336 self.encoding = get_input_encoding()
336 337
337 338 def reset(self):
338 339 """Reset the input buffer and associated state."""
339 340 self._buffer[:] = []
340 341 self.source = ''
341 342 self.code = None
342 343 self._is_complete = False
343 344 self._is_invalid = False
344 345
345 346 def source_reset(self):
346 347 """Return the input source and perform a full reset.
347 348 """
348 349 out = self.source
349 350 self.reset()
350 351 return out
351 352
352 353 def check_complete(self, source):
353 354 """Return whether a block of code is ready to execute, or should be continued
354 355
355 356 This is a non-stateful API, and will reset the state of this InputSplitter.
356 357
357 358 Parameters
358 359 ----------
359 360 source : string
360 361 Python input code, which can be multiline.
361 362
362 363 Returns
363 364 -------
364 365 status : str
365 366 One of 'complete', 'incomplete', or 'invalid' if source is not a
366 367 prefix of valid code.
367 368 indent_spaces : int or None
368 369 The number of spaces by which to indent the next line of code. If
369 370 status is not 'incomplete', this is None.
370 371 """
371 372 self.reset()
372 373 try:
373 374 self.push(source)
374 375 except SyntaxError:
375 376 # Transformers in IPythonInputSplitter can raise SyntaxError,
376 377 # which push() will not catch.
377 378 return 'invalid', None
378 379 else:
379 380 if self._is_invalid:
380 381 return 'invalid', None
381 382 elif self.push_accepts_more():
382 383 return 'incomplete', self.get_indent_spaces()
383 384 else:
384 385 return 'complete', None
385 386 finally:
386 387 self.reset()
387 388
388 389 def push(self, lines:str) -> bool:
389 390 """Push one or more lines of input.
390 391
391 392 This stores the given lines and returns a status code indicating
392 393 whether the code forms a complete Python block or not.
393 394
394 395 Any exceptions generated in compilation are swallowed, but if an
395 396 exception was produced, the method returns True.
396 397
397 398 Parameters
398 399 ----------
399 400 lines : string
400 401 One or more lines of Python input.
401 402
402 403 Returns
403 404 -------
404 405 is_complete : boolean
405 406 True if the current input source (the result of the current input
406 407 plus prior inputs) forms a complete Python execution block. Note that
407 408 this value is also stored as a private attribute (``_is_complete``), so it
408 409 can be queried at any time.
409 410 """
410 411 assert isinstance(lines, str)
411 412 self._store(lines)
412 413 source = self.source
413 414
414 415 # Before calling _compile(), reset the code object to None so that if an
415 416 # exception is raised in compilation, we don't mislead by having
416 417 # inconsistent code/source attributes.
417 418 self.code, self._is_complete = None, None
418 419 self._is_invalid = False
419 420
420 421 # Honor termination lines properly
421 422 if source.endswith('\\\n'):
422 423 return False
423 424
424 425 try:
425 426 with warnings.catch_warnings():
426 427 warnings.simplefilter('error', SyntaxWarning)
427 428 self.code = self._compile(source, symbol="exec")
428 429 # Invalid syntax can produce any of a number of different errors from
429 430 # inside the compiler, so we have to catch them all. Syntax errors
430 431 # immediately produce a 'ready' block, so the invalid Python can be
431 432 # sent to the kernel for evaluation with possible ipython
432 433 # special-syntax conversion.
433 434 except (SyntaxError, OverflowError, ValueError, TypeError,
434 435 MemoryError, SyntaxWarning):
435 436 self._is_complete = True
436 437 self._is_invalid = True
437 438 else:
438 439 # Compilation didn't produce any exceptions (though it may not have
439 440 # given a complete code object)
440 441 self._is_complete = self.code is not None
441 442
442 443 return self._is_complete
443 444
444 445 def push_accepts_more(self):
445 446 """Return whether a block of interactive input can accept more input.
446 447
447 448 This method is meant to be used by line-oriented frontends, who need to
448 449 guess whether a block is complete or not based solely on prior and
449 450 current input lines. The InputSplitter considers it has a complete
450 451 interactive block and will not accept more input when either:
451 452
452 453 * A SyntaxError is raised
453 454
454 455 * The code is complete and consists of a single line or a single
455 456 non-compound statement
456 457
457 458 * The code is complete and has a blank line at the end
458 459
459 460 If the current input produces a syntax error, this method immediately
460 461 returns False but does *not* raise the syntax error exception, as
461 462 typically clients will want to send invalid syntax to an execution
462 463 backend which might convert the invalid syntax into valid Python via
463 464 one of the dynamic IPython mechanisms.
464 465 """
465 466
466 467 # With incomplete input, unconditionally accept more
467 468 # A syntax error also sets _is_complete to True - see push()
468 469 if not self._is_complete:
469 470 #print("Not complete") # debug
470 471 return True
471 472
472 473 # The user can make any (complete) input execute by leaving a blank line
473 474 last_line = self.source.splitlines()[-1]
474 475 if (not last_line) or last_line.isspace():
475 476 #print("Blank line") # debug
476 477 return False
477 478
478 479 # If there's just a single line or AST node, and we're flush left, as is
479 480 # the case after a simple statement such as 'a=1', we want to execute it
480 481 # straight away.
481 482 if self.get_indent_spaces() == 0:
482 483 if len(self.source.splitlines()) <= 1:
483 484 return False
484 485
485 486 try:
486 code_ast = ast.parse(u''.join(self._buffer))
487 code_ast = ast.parse("".join(self._buffer))
487 488 except Exception:
488 489 #print("Can't parse AST") # debug
489 490 return False
490 491 else:
491 492 if len(code_ast.body) == 1 and \
492 493 not hasattr(code_ast.body[0], 'body'):
493 494 #print("Simple statement") # debug
494 495 return False
495 496
496 497 # General fallback - accept more code
497 498 return True
498 499
499 500 def get_indent_spaces(self):
500 501 sourcefor, n = self._indent_spaces_cache
501 502 if sourcefor == self.source:
502 503 return n
503 504
504 505 # self.source always has a trailing newline
505 506 n = find_next_indent(self.source[:-1])
506 507 self._indent_spaces_cache = (self.source, n)
507 508 return n
508 509
509 510 # Backwards compatibility. I think all code that used .indent_spaces was
510 511 # inside IPython, but we can leave this here until IPython 7 in case any
511 512 # other modules are using it. -TK, November 2017
512 513 indent_spaces = property(get_indent_spaces)
513 514
514 515 def _store(self, lines, buffer=None, store='source'):
515 516 """Store one or more lines of input.
516 517
517 518 If input lines are not newline-terminated, a newline is automatically
518 519 appended."""
519 520
520 521 if buffer is None:
521 522 buffer = self._buffer
522 523
523 524 if lines.endswith('\n'):
524 525 buffer.append(lines)
525 526 else:
526 527 buffer.append(lines+'\n')
527 528 setattr(self, store, self._set_source(buffer))
528 529
529 530 def _set_source(self, buffer):
530 531 return u''.join(buffer)
531 532
532 533
533 534 class IPythonInputSplitter(InputSplitter):
534 535 """An input splitter that recognizes all of IPython's special syntax."""
535 536
536 537 # String with raw, untransformed input.
537 538 source_raw = ''
538 539
539 540 # Flag to track when a transformer has stored input that it hasn't given
540 541 # back yet.
541 542 transformer_accumulating = False
542 543
543 544 # Flag to track when assemble_python_lines has stored input that it hasn't
544 545 # given back yet.
545 546 within_python_line = False
546 547
547 548 # Private attributes
548 549
549 550 # List with lines of raw input accumulated so far.
550 551 _buffer_raw = None
551 552
552 553 def __init__(self, line_input_checker=True, physical_line_transforms=None,
553 554 logical_line_transforms=None, python_line_transforms=None):
554 555 super(IPythonInputSplitter, self).__init__()
555 556 self._buffer_raw = []
556 557 self._validate = True
557 558
558 559 if physical_line_transforms is not None:
559 560 self.physical_line_transforms = physical_line_transforms
560 561 else:
561 562 self.physical_line_transforms = [
562 563 leading_indent(),
563 564 classic_prompt(),
564 565 ipy_prompt(),
565 566 cellmagic(end_on_blank_line=line_input_checker),
566 567 ]
567 568
568 569 self.assemble_logical_lines = assemble_logical_lines()
569 570 if logical_line_transforms is not None:
570 571 self.logical_line_transforms = logical_line_transforms
571 572 else:
572 573 self.logical_line_transforms = [
573 574 help_end(),
574 575 escaped_commands(),
575 576 assign_from_magic(),
576 577 assign_from_system(),
577 578 ]
578 579
579 580 self.assemble_python_lines = assemble_python_lines()
580 581 if python_line_transforms is not None:
581 582 self.python_line_transforms = python_line_transforms
582 583 else:
583 584 # We don't use any of these at present
584 585 self.python_line_transforms = []
585 586
586 587 @property
587 588 def transforms(self):
588 589 "Quick access to all transformers."
589 590 return self.physical_line_transforms + \
590 591 [self.assemble_logical_lines] + self.logical_line_transforms + \
591 592 [self.assemble_python_lines] + self.python_line_transforms
592 593
593 594 @property
594 595 def transforms_in_use(self):
595 596 """Transformers, excluding logical line transformers if we're in a
596 597 Python line."""
597 598 t = self.physical_line_transforms[:]
598 599 if not self.within_python_line:
599 600 t += [self.assemble_logical_lines] + self.logical_line_transforms
600 601 return t + [self.assemble_python_lines] + self.python_line_transforms
601 602
602 603 def reset(self):
603 604 """Reset the input buffer and associated state."""
604 605 super(IPythonInputSplitter, self).reset()
605 606 self._buffer_raw[:] = []
606 607 self.source_raw = ''
607 608 self.transformer_accumulating = False
608 609 self.within_python_line = False
609 610
610 611 for t in self.transforms:
611 612 try:
612 613 t.reset()
613 614 except SyntaxError:
614 615 # Nothing that calls reset() expects to handle transformer
615 616 # errors
616 617 pass
617 618
618 619 def flush_transformers(self):
619 620 def _flush(transform, outs):
620 621 """yield transformed lines
621 622
622 623 always strings, never None
623 624
624 625 transform: the current transform
625 626 outs: an iterable of previously transformed inputs.
626 627 Each may be multiline, which will be passed
627 628 one line at a time to transform.
628 629 """
629 630 for out in outs:
630 631 for line in out.splitlines():
631 632 # push one line at a time
632 633 tmp = transform.push(line)
633 634 if tmp is not None:
634 635 yield tmp
635 636
636 637 # reset the transform
637 638 tmp = transform.reset()
638 639 if tmp is not None:
639 640 yield tmp
640 641
641 642 out = []
642 643 for t in self.transforms_in_use:
643 644 out = _flush(t, out)
644 645
645 646 out = list(out)
646 647 if out:
647 648 self._store('\n'.join(out))
648 649
649 650 def raw_reset(self):
650 651 """Return raw input only and perform a full reset.
651 652 """
652 653 out = self.source_raw
653 654 self.reset()
654 655 return out
655 656
656 657 def source_reset(self):
657 658 try:
658 659 self.flush_transformers()
659 660 return self.source
660 661 finally:
661 662 self.reset()
662 663
663 664 def push_accepts_more(self):
664 665 if self.transformer_accumulating:
665 666 return True
666 667 else:
667 668 return super(IPythonInputSplitter, self).push_accepts_more()
668 669
669 670 def transform_cell(self, cell):
670 671 """Process and translate a cell of input.
671 672 """
672 673 self.reset()
673 674 try:
674 675 self.push(cell)
675 676 self.flush_transformers()
676 677 return self.source
677 678 finally:
678 679 self.reset()
679 680
680 681 def push(self, lines:str) -> bool:
681 682 """Push one or more lines of IPython input.
682 683
683 684 This stores the given lines and returns a status code indicating
684 685 whether the code forms a complete Python block or not, after processing
685 686 all input lines for special IPython syntax.
686 687
687 688 Any exceptions generated in compilation are swallowed, but if an
688 689 exception was produced, the method returns True.
689 690
690 691 Parameters
691 692 ----------
692 693 lines : string
693 694 One or more lines of Python input.
694 695
695 696 Returns
696 697 -------
697 698 is_complete : boolean
698 699 True if the current input source (the result of the current input
699 700 plus prior inputs) forms a complete Python execution block. Note that
700 701 this value is also stored as a private attribute (_is_complete), so it
701 702 can be queried at any time.
702 703 """
703 704 assert isinstance(lines, str)
704 705 # We must ensure all input is pure unicode
705 706 # ''.splitlines() --> [], but we need to push the empty line to transformers
706 707 lines_list = lines.splitlines()
707 708 if not lines_list:
708 709 lines_list = ['']
709 710
710 711 # Store raw source before applying any transformations to it. Note
711 712 # that this must be done *after* the reset() call that would otherwise
712 713 # flush the buffer.
713 714 self._store(lines, self._buffer_raw, 'source_raw')
714 715
715 716 transformed_lines_list = []
716 717 for line in lines_list:
717 718 transformed = self._transform_line(line)
718 719 if transformed is not None:
719 720 transformed_lines_list.append(transformed)
720 721
721 722 if transformed_lines_list:
722 723 transformed_lines = '\n'.join(transformed_lines_list)
723 724 return super(IPythonInputSplitter, self).push(transformed_lines)
724 725 else:
725 726 # Got nothing back from transformers - they must be waiting for
726 727 # more input.
727 728 return False
728 729
729 730 def _transform_line(self, line):
730 731 """Push a line of input code through the various transformers.
731 732
732 733 Returns any output from the transformers, or None if a transformer
733 734 is accumulating lines.
734 735
735 736 Sets self.transformer_accumulating as a side effect.
736 737 """
737 738 def _accumulating(dbg):
738 739 #print(dbg)
739 740 self.transformer_accumulating = True
740 741 return None
741 742
742 743 for transformer in self.physical_line_transforms:
743 744 line = transformer.push(line)
744 745 if line is None:
745 746 return _accumulating(transformer)
746 747
747 748 if not self.within_python_line:
748 749 line = self.assemble_logical_lines.push(line)
749 750 if line is None:
750 751 return _accumulating('acc logical line')
751 752
752 753 for transformer in self.logical_line_transforms:
753 754 line = transformer.push(line)
754 755 if line is None:
755 756 return _accumulating(transformer)
756 757
757 758 line = self.assemble_python_lines.push(line)
758 759 if line is None:
759 760 self.within_python_line = True
760 761 return _accumulating('acc python line')
761 762 else:
762 763 self.within_python_line = False
763 764
764 765 for transformer in self.python_line_transforms:
765 766 line = transformer.push(line)
766 767 if line is None:
767 768 return _accumulating(transformer)
768 769
769 770 #print("transformers clear") #debug
770 771 self.transformer_accumulating = False
771 772 return line
772 773
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,310 +1,310 b''
1 1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 2 option handling.
3 3
4 4 New magic functions can be defined like so::
5 5
6 6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 7 parse_argstring)
8 8
9 9 @magic_arguments()
10 10 @argument('-o', '--option', help='An optional argument.')
11 11 @argument('arg', type=int, help='An integer positional argument.')
12 12 def magic_cool(self, arg):
13 13 """ A really cool magic command.
14 14
15 15 """
16 16 args = parse_argstring(magic_cool, arg)
17 17 ...
18 18
19 19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 21 `add_argument()` method. More sophisticated uses may also require the
22 22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 23 parsing.
24 24
25 25 Help text for the magic is automatically generated from the docstring and the
26 26 arguments::
27 27
28 28 In[1]: %cool?
29 29 %cool [-o OPTION] arg
30 30
31 31 A really cool magic command.
32 32
33 33 positional arguments:
34 34 arg An integer positional argument.
35 35
36 36 optional arguments:
37 37 -o OPTION, --option OPTION
38 38 An optional argument.
39 39
40 40 Here is an elaborated example that uses default parameters in `argument` and calls the `args` in the cell magic::
41 41
42 42 from IPython.core.magic import register_cell_magic
43 43 from IPython.core.magic_arguments import (argument, magic_arguments,
44 44 parse_argstring)
45 45
46 46
47 47 @magic_arguments()
48 48 @argument(
49 49 "--option",
50 50 "-o",
51 51 help=("Add an option here"),
52 52 )
53 53 @argument(
54 54 "--style",
55 55 "-s",
56 56 default="foo",
57 57 help=("Add some style arguments"),
58 58 )
59 59 @register_cell_magic
60 60 def my_cell_magic(line, cell):
61 61 args = parse_argstring(my_cell_magic, line)
62 62 print(f"{args.option=}")
63 63 print(f"{args.style=}")
64 64 print(f"{cell=}")
65 65
66 66 In a jupyter notebook, this cell magic can be executed like this::
67 67
68 68 %%my_cell_magic -o Hello
69 69 print("bar")
70 70 i = 42
71 71
72 72 Inheritance diagram:
73 73
74 74 .. inheritance-diagram:: IPython.core.magic_arguments
75 75 :parts: 3
76 76
77 77 '''
78 78 #-----------------------------------------------------------------------------
79 79 # Copyright (C) 2010-2011, IPython Development Team.
80 80 #
81 81 # Distributed under the terms of the Modified BSD License.
82 82 #
83 83 # The full license is in the file COPYING.txt, distributed with this software.
84 84 #-----------------------------------------------------------------------------
85 85 import argparse
86 86 import re
87 87
88 88 # Our own imports
89 89 from IPython.core.error import UsageError
90 90 from IPython.utils.decorators import undoc
91 91 from IPython.utils.process import arg_split
92 92 from IPython.utils.text import dedent
93 93
94 94 NAME_RE = re.compile(r"[a-zA-Z][a-zA-Z0-9_-]*$")
95 95
96 96 @undoc
97 97 class MagicHelpFormatter(argparse.RawDescriptionHelpFormatter):
98 98 """A HelpFormatter with a couple of changes to meet our needs.
99 99 """
100 100 # Modified to dedent text.
101 101 def _fill_text(self, text, width, indent):
102 102 return argparse.RawDescriptionHelpFormatter._fill_text(self, dedent(text), width, indent)
103 103
104 104 # Modified to wrap argument placeholders in <> where necessary.
105 105 def _format_action_invocation(self, action):
106 106 if not action.option_strings:
107 107 metavar, = self._metavar_formatter(action, action.dest)(1)
108 108 return metavar
109 109
110 110 else:
111 111 parts = []
112 112
113 113 # if the Optional doesn't take a value, format is:
114 114 # -s, --long
115 115 if action.nargs == 0:
116 116 parts.extend(action.option_strings)
117 117
118 118 # if the Optional takes a value, format is:
119 119 # -s ARGS, --long ARGS
120 120 else:
121 121 default = action.dest.upper()
122 122 args_string = self._format_args(action, default)
123 123 # IPYTHON MODIFICATION: If args_string is not a plain name, wrap
124 124 # it in <> so it's valid RST.
125 125 if not NAME_RE.match(args_string):
126 126 args_string = "<%s>" % args_string
127 127 for option_string in action.option_strings:
128 128 parts.append('%s %s' % (option_string, args_string))
129 129
130 130 return ', '.join(parts)
131 131
132 132 # Override the default prefix ('usage') to our % magic escape,
133 133 # in a code block.
134 134 def add_usage(self, usage, actions, groups, prefix="::\n\n %"):
135 135 super(MagicHelpFormatter, self).add_usage(usage, actions, groups, prefix)
136 136
137 137 class MagicArgumentParser(argparse.ArgumentParser):
138 138 """ An ArgumentParser tweaked for use by IPython magics.
139 139 """
140 140 def __init__(self,
141 141 prog=None,
142 142 usage=None,
143 143 description=None,
144 144 epilog=None,
145 145 parents=None,
146 146 formatter_class=MagicHelpFormatter,
147 147 prefix_chars='-',
148 148 argument_default=None,
149 149 conflict_handler='error',
150 150 add_help=False):
151 151 if parents is None:
152 152 parents = []
153 153 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
154 154 description=description, epilog=epilog,
155 155 parents=parents, formatter_class=formatter_class,
156 156 prefix_chars=prefix_chars, argument_default=argument_default,
157 157 conflict_handler=conflict_handler, add_help=add_help)
158 158
159 159 def error(self, message):
160 160 """ Raise a catchable error instead of exiting.
161 161 """
162 162 raise UsageError(message)
163 163
164 164 def parse_argstring(self, argstring):
165 165 """ Split a string into an argument list and parse that argument list.
166 166 """
167 167 argv = arg_split(argstring)
168 168 return self.parse_args(argv)
169 169
170 170
171 171 def construct_parser(magic_func):
172 172 """ Construct an argument parser using the function decorations.
173 173 """
174 174 kwds = getattr(magic_func, 'argcmd_kwds', {})
175 175 if 'description' not in kwds:
176 176 kwds['description'] = getattr(magic_func, '__doc__', None)
177 177 arg_name = real_name(magic_func)
178 178 parser = MagicArgumentParser(arg_name, **kwds)
179 179 # Reverse the list of decorators in order to apply them in the
180 180 # order in which they appear in the source.
181 181 group = None
182 182 for deco in magic_func.decorators[::-1]:
183 183 result = deco.add_to_parser(parser, group)
184 184 if result is not None:
185 185 group = result
186 186
187 187 # Replace the magic function's docstring with the full help text.
188 188 magic_func.__doc__ = parser.format_help()
189 189
190 190 return parser
191 191
192 192
193 193 def parse_argstring(magic_func, argstring):
194 194 """ Parse the string of arguments for the given magic function.
195 195 """
196 196 return magic_func.parser.parse_argstring(argstring)
197 197
198 198
199 199 def real_name(magic_func):
200 200 """ Find the real name of the magic.
201 201 """
202 202 magic_name = magic_func.__name__
203 203 if magic_name.startswith('magic_'):
204 204 magic_name = magic_name[len('magic_'):]
205 205 return getattr(magic_func, 'argcmd_name', magic_name)
206 206
207 207
208 208 class ArgDecorator(object):
209 209 """ Base class for decorators to add ArgumentParser information to a method.
210 210 """
211 211
212 212 def __call__(self, func):
213 213 if not getattr(func, 'has_arguments', False):
214 214 func.has_arguments = True
215 215 func.decorators = []
216 216 func.decorators.append(self)
217 217 return func
218 218
219 219 def add_to_parser(self, parser, group):
220 220 """ Add this object's information to the parser, if necessary.
221 221 """
222 222 pass
223 223
224 224
225 225 class magic_arguments(ArgDecorator):
226 226 """ Mark the magic as having argparse arguments and possibly adjust the
227 227 name.
228 228 """
229 229
230 230 def __init__(self, name=None):
231 231 self.name = name
232 232
233 233 def __call__(self, func):
234 234 if not getattr(func, 'has_arguments', False):
235 235 func.has_arguments = True
236 236 func.decorators = []
237 237 if self.name is not None:
238 238 func.argcmd_name = self.name
239 239 # This should be the first decorator in the list of decorators, thus the
240 240 # last to execute. Build the parser.
241 241 func.parser = construct_parser(func)
242 242 return func
243 243
244 244
245 245 class ArgMethodWrapper(ArgDecorator):
246 246
247 247 """
248 248 Base class to define a wrapper for ArgumentParser method.
249 249
250 250 Child class must define either `_method_name` or `add_to_parser`.
251 251
252 252 """
253 253
254 _method_name = None
254 _method_name: str
255 255
256 256 def __init__(self, *args, **kwds):
257 257 self.args = args
258 258 self.kwds = kwds
259 259
260 260 def add_to_parser(self, parser, group):
261 261 """ Add this object's information to the parser.
262 262 """
263 263 if group is not None:
264 264 parser = group
265 265 getattr(parser, self._method_name)(*self.args, **self.kwds)
266 266 return None
267 267
268 268
269 269 class argument(ArgMethodWrapper):
270 270 """ Store arguments and keywords to pass to add_argument().
271 271
272 272 Instances also serve to decorate command methods.
273 273 """
274 274 _method_name = 'add_argument'
275 275
276 276
277 277 class defaults(ArgMethodWrapper):
278 278 """ Store arguments and keywords to pass to set_defaults().
279 279
280 280 Instances also serve to decorate command methods.
281 281 """
282 282 _method_name = 'set_defaults'
283 283
284 284
285 285 class argument_group(ArgMethodWrapper):
286 286 """ Store arguments and keywords to pass to add_argument_group().
287 287
288 288 Instances also serve to decorate command methods.
289 289 """
290 290
291 291 def add_to_parser(self, parser, group):
292 292 """ Add this object's information to the parser.
293 293 """
294 294 return parser.add_argument_group(*self.args, **self.kwds)
295 295
296 296
297 297 class kwds(ArgDecorator):
298 298 """ Provide other keywords to the sub-parser constructor.
299 299 """
300 300 def __init__(self, **kwds):
301 301 self.kwds = kwds
302 302
303 303 def __call__(self, func):
304 304 func = super(kwds, self).__call__(func)
305 305 func.argcmd_kwds = self.kwds
306 306 return func
307 307
308 308
309 309 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
310 310 'parse_argstring']
@@ -1,1093 +1,1098 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for inspecting Python objects.
3 3
4 4 Uses syntax highlighting for presenting the various information elements.
5 5
6 6 Similar in spirit to the inspect module, but all calls take a name argument to
7 7 reference the name under which an object is being read.
8 8 """
9 9
10 10 # Copyright (c) IPython Development Team.
11 11 # Distributed under the terms of the Modified BSD License.
12 12
13 13 __all__ = ['Inspector','InspectColors']
14 14
15 15 # stdlib modules
16 16 import ast
17 17 import inspect
18 18 from inspect import signature
19 19 import html
20 20 import linecache
21 21 import warnings
22 22 import os
23 23 from textwrap import dedent
24 24 import types
25 25 import io as stdlib_io
26 26
27 27 from typing import Union
28 28
29 29 # IPython's own
30 30 from IPython.core import page
31 31 from IPython.lib.pretty import pretty
32 32 from IPython.testing.skipdoctest import skip_doctest
33 33 from IPython.utils import PyColorize
34 34 from IPython.utils import openpy
35 35 from IPython.utils.dir2 import safe_hasattr
36 36 from IPython.utils.path import compress_user
37 37 from IPython.utils.text import indent
38 38 from IPython.utils.wildcard import list_namespace
39 39 from IPython.utils.wildcard import typestr2type
40 40 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
41 41 from IPython.utils.py3compat import cast_unicode
42 42 from IPython.utils.colorable import Colorable
43 43 from IPython.utils.decorators import undoc
44 44
45 45 from pygments import highlight
46 46 from pygments.lexers import PythonLexer
47 47 from pygments.formatters import HtmlFormatter
48 48
49 from typing import Any
49 from typing import Any, Optional
50 50 from dataclasses import dataclass
51 51
52 52
53 53 @dataclass
54 54 class OInfo:
55 55 ismagic: bool
56 56 isalias: bool
57 57 found: bool
58 namespace: str
58 namespace: Optional[str]
59 59 parent: Any
60 60 obj: Any
61 61
62 62 def pylight(code):
63 63 return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
64 64
65 65 # builtin docstrings to ignore
66 66 _func_call_docstring = types.FunctionType.__call__.__doc__
67 67 _object_init_docstring = object.__init__.__doc__
68 68 _builtin_type_docstrings = {
69 69 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
70 70 types.FunctionType, property)
71 71 }
72 72
73 73 _builtin_func_type = type(all)
74 74 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
75 75 #****************************************************************************
76 76 # Builtin color schemes
77 77
78 78 Colors = TermColors # just a shorthand
79 79
80 80 InspectColors = PyColorize.ANSICodeColors
81 81
82 82 #****************************************************************************
83 83 # Auxiliary functions and objects
84 84
85 85 # See the messaging spec for the definition of all these fields. This list
86 86 # effectively defines the order of display
87 87 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
88 88 'length', 'file', 'definition', 'docstring', 'source',
89 89 'init_definition', 'class_docstring', 'init_docstring',
90 90 'call_def', 'call_docstring',
91 91 # These won't be printed but will be used to determine how to
92 92 # format the object
93 93 'ismagic', 'isalias', 'isclass', 'found', 'name'
94 94 ]
95 95
96 96
97 97 def object_info(**kw):
98 98 """Make an object info dict with all fields present."""
99 99 infodict = {k:None for k in info_fields}
100 100 infodict.update(kw)
101 101 return infodict
102 102
103 103
104 104 def get_encoding(obj):
105 105 """Get encoding for python source file defining obj
106 106
107 107 Returns None if obj is not defined in a sourcefile.
108 108 """
109 109 ofile = find_file(obj)
110 110 # run contents of file through pager starting at line where the object
111 111 # is defined, as long as the file isn't binary and is actually on the
112 112 # filesystem.
113 113 if ofile is None:
114 114 return None
115 115 elif ofile.endswith(('.so', '.dll', '.pyd')):
116 116 return None
117 117 elif not os.path.isfile(ofile):
118 118 return None
119 119 else:
120 120 # Print only text files, not extension binaries. Note that
121 121 # getsourcelines returns lineno with 1-offset and page() uses
122 122 # 0-offset, so we must adjust.
123 123 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
124 124 encoding, lines = openpy.detect_encoding(buffer.readline)
125 125 return encoding
126 126
127 127 def getdoc(obj) -> Union[str,None]:
128 128 """Stable wrapper around inspect.getdoc.
129 129
130 130 This can't crash because of attribute problems.
131 131
132 132 It also attempts to call a getdoc() method on the given object. This
133 133 allows objects which provide their docstrings via non-standard mechanisms
134 134 (like Pyro proxies) to still be inspected by ipython's ? system.
135 135 """
136 136 # Allow objects to offer customized documentation via a getdoc method:
137 137 try:
138 138 ds = obj.getdoc()
139 139 except Exception:
140 140 pass
141 141 else:
142 142 if isinstance(ds, str):
143 143 return inspect.cleandoc(ds)
144 144 docstr = inspect.getdoc(obj)
145 145 return docstr
146 146
147 147
148 148 def getsource(obj, oname='') -> Union[str,None]:
149 149 """Wrapper around inspect.getsource.
150 150
151 151 This can be modified by other projects to provide customized source
152 152 extraction.
153 153
154 154 Parameters
155 155 ----------
156 156 obj : object
157 157 an object whose source code we will attempt to extract
158 158 oname : str
159 159 (optional) a name under which the object is known
160 160
161 161 Returns
162 162 -------
163 163 src : unicode or None
164 164
165 165 """
166 166
167 167 if isinstance(obj, property):
168 168 sources = []
169 169 for attrname in ['fget', 'fset', 'fdel']:
170 170 fn = getattr(obj, attrname)
171 171 if fn is not None:
172 172 encoding = get_encoding(fn)
173 173 oname_prefix = ('%s.' % oname) if oname else ''
174 174 sources.append(''.join(('# ', oname_prefix, attrname)))
175 175 if inspect.isfunction(fn):
176 sources.append(dedent(getsource(fn)))
176 _src = getsource(fn)
177 if _src:
178 # assert _src is not None, "please mypy"
179 sources.append(dedent(_src))
177 180 else:
178 181 # Default str/repr only prints function name,
179 182 # pretty.pretty prints module name too.
180 183 sources.append(
181 184 '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
182 185 )
183 186 if sources:
184 187 return '\n'.join(sources)
185 188 else:
186 189 return None
187 190
188 191 else:
189 192 # Get source for non-property objects.
190 193
191 194 obj = _get_wrapped(obj)
192 195
193 196 try:
194 197 src = inspect.getsource(obj)
195 198 except TypeError:
196 199 # The object itself provided no meaningful source, try looking for
197 200 # its class definition instead.
198 201 try:
199 202 src = inspect.getsource(obj.__class__)
200 203 except (OSError, TypeError):
201 204 return None
202 205 except OSError:
203 206 return None
204 207
205 208 return src
206 209
207 210
208 211 def is_simple_callable(obj):
209 212 """True if obj is a function ()"""
210 213 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
211 214 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
212 215
213 216 @undoc
214 217 def getargspec(obj):
215 218 """Wrapper around :func:`inspect.getfullargspec`
216 219
217 220 In addition to functions and methods, this can also handle objects with a
218 221 ``__call__`` attribute.
219 222
220 223 DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
221 224 """
222 225
223 226 warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
224 227 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
225 228
226 229 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
227 230 obj = obj.__call__
228 231
229 232 return inspect.getfullargspec(obj)
230 233
231 234 @undoc
232 235 def format_argspec(argspec):
233 236 """Format argspect, convenience wrapper around inspect's.
234 237
235 238 This takes a dict instead of ordered arguments and calls
236 239 inspect.format_argspec with the arguments in the necessary order.
237 240
238 241 DEPRECATED (since 7.10): Do not use; will be removed in future versions.
239 242 """
240 243
241 244 warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
242 245 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
243 246
244 247
245 248 return inspect.formatargspec(argspec['args'], argspec['varargs'],
246 249 argspec['varkw'], argspec['defaults'])
247 250
248 251 @undoc
249 252 def call_tip(oinfo, format_call=True):
250 253 """DEPRECATED since 6.0. Extract call tip data from an oinfo dict."""
251 254 warnings.warn(
252 255 "`call_tip` function is deprecated as of IPython 6.0"
253 256 "and will be removed in future versions.",
254 257 DeprecationWarning,
255 258 stacklevel=2,
256 259 )
257 260 # Get call definition
258 261 argspec = oinfo.get('argspec')
259 262 if argspec is None:
260 263 call_line = None
261 264 else:
262 265 # Callable objects will have 'self' as their first argument, prune
263 266 # it out if it's there for clarity (since users do *not* pass an
264 267 # extra first argument explicitly).
265 268 try:
266 269 has_self = argspec['args'][0] == 'self'
267 270 except (KeyError, IndexError):
268 271 pass
269 272 else:
270 273 if has_self:
271 274 argspec['args'] = argspec['args'][1:]
272 275
273 276 call_line = oinfo['name']+format_argspec(argspec)
274 277
275 278 # Now get docstring.
276 279 # The priority is: call docstring, constructor docstring, main one.
277 280 doc = oinfo.get('call_docstring')
278 281 if doc is None:
279 282 doc = oinfo.get('init_docstring')
280 283 if doc is None:
281 284 doc = oinfo.get('docstring','')
282 285
283 286 return call_line, doc
284 287
285 288
286 289 def _get_wrapped(obj):
287 290 """Get the original object if wrapped in one or more @decorators
288 291
289 292 Some objects automatically construct similar objects on any unrecognised
290 293 attribute access (e.g. unittest.mock.call). To protect against infinite loops,
291 294 this will arbitrarily cut off after 100 levels of obj.__wrapped__
292 295 attribute access. --TK, Jan 2016
293 296 """
294 297 orig_obj = obj
295 298 i = 0
296 299 while safe_hasattr(obj, '__wrapped__'):
297 300 obj = obj.__wrapped__
298 301 i += 1
299 302 if i > 100:
300 303 # __wrapped__ is probably a lie, so return the thing we started with
301 304 return orig_obj
302 305 return obj
303 306
304 307 def find_file(obj) -> str:
305 308 """Find the absolute path to the file where an object was defined.
306 309
307 310 This is essentially a robust wrapper around `inspect.getabsfile`.
308 311
309 312 Returns None if no file can be found.
310 313
311 314 Parameters
312 315 ----------
313 316 obj : any Python object
314 317
315 318 Returns
316 319 -------
317 320 fname : str
318 321 The absolute path to the file where the object was defined.
319 322 """
320 323 obj = _get_wrapped(obj)
321 324
322 325 fname = None
323 326 try:
324 327 fname = inspect.getabsfile(obj)
325 328 except TypeError:
326 329 # For an instance, the file that matters is where its class was
327 330 # declared.
328 331 try:
329 332 fname = inspect.getabsfile(obj.__class__)
330 333 except (OSError, TypeError):
331 334 # Can happen for builtins
332 335 pass
333 336 except OSError:
334 337 pass
335 338
336 339 return cast_unicode(fname)
337 340
338 341
339 342 def find_source_lines(obj):
340 343 """Find the line number in a file where an object was defined.
341 344
342 345 This is essentially a robust wrapper around `inspect.getsourcelines`.
343 346
344 347 Returns None if no file can be found.
345 348
346 349 Parameters
347 350 ----------
348 351 obj : any Python object
349 352
350 353 Returns
351 354 -------
352 355 lineno : int
353 356 The line number where the object definition starts.
354 357 """
355 358 obj = _get_wrapped(obj)
356 359
357 360 try:
358 361 lineno = inspect.getsourcelines(obj)[1]
359 362 except TypeError:
360 363 # For instances, try the class object like getsource() does
361 364 try:
362 365 lineno = inspect.getsourcelines(obj.__class__)[1]
363 366 except (OSError, TypeError):
364 367 return None
365 368 except OSError:
366 369 return None
367 370
368 371 return lineno
369 372
370 373 class Inspector(Colorable):
371 374
372 375 def __init__(self, color_table=InspectColors,
373 376 code_color_table=PyColorize.ANSICodeColors,
374 377 scheme=None,
375 378 str_detail_level=0,
376 379 parent=None, config=None):
377 380 super(Inspector, self).__init__(parent=parent, config=config)
378 381 self.color_table = color_table
379 382 self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
380 383 self.format = self.parser.format
381 384 self.str_detail_level = str_detail_level
382 385 self.set_active_scheme(scheme)
383 386
384 387 def _getdef(self,obj,oname='') -> Union[str,None]:
385 388 """Return the call signature for any callable object.
386 389
387 390 If any exception is generated, None is returned instead and the
388 391 exception is suppressed."""
389 392 try:
390 393 return _render_signature(signature(obj), oname)
391 394 except:
392 395 return None
393 396
394 397 def __head(self,h) -> str:
395 398 """Return a header string with proper colors."""
396 399 return '%s%s%s' % (self.color_table.active_colors.header,h,
397 400 self.color_table.active_colors.normal)
398 401
399 402 def set_active_scheme(self, scheme):
400 403 if scheme is not None:
401 404 self.color_table.set_active_scheme(scheme)
402 405 self.parser.color_table.set_active_scheme(scheme)
403 406
404 407 def noinfo(self, msg, oname):
405 408 """Generic message when no information is found."""
406 409 print('No %s found' % msg, end=' ')
407 410 if oname:
408 411 print('for %s' % oname)
409 412 else:
410 413 print()
411 414
412 415 def pdef(self, obj, oname=''):
413 416 """Print the call signature for any callable object.
414 417
415 418 If the object is a class, print the constructor information."""
416 419
417 420 if not callable(obj):
418 421 print('Object is not callable.')
419 422 return
420 423
421 424 header = ''
422 425
423 426 if inspect.isclass(obj):
424 427 header = self.__head('Class constructor information:\n')
425 428
426 429
427 430 output = self._getdef(obj,oname)
428 431 if output is None:
429 432 self.noinfo('definition header',oname)
430 433 else:
431 434 print(header,self.format(output), end=' ')
432 435
433 436 # In Python 3, all classes are new-style, so they all have __init__.
434 437 @skip_doctest
435 438 def pdoc(self, obj, oname='', formatter=None):
436 439 """Print the docstring for any object.
437 440
438 441 Optional:
439 442 -formatter: a function to run the docstring through for specially
440 443 formatted docstrings.
441 444
442 445 Examples
443 446 --------
444 447 In [1]: class NoInit:
445 448 ...: pass
446 449
447 450 In [2]: class NoDoc:
448 451 ...: def __init__(self):
449 452 ...: pass
450 453
451 454 In [3]: %pdoc NoDoc
452 455 No documentation found for NoDoc
453 456
454 457 In [4]: %pdoc NoInit
455 458 No documentation found for NoInit
456 459
457 460 In [5]: obj = NoInit()
458 461
459 462 In [6]: %pdoc obj
460 463 No documentation found for obj
461 464
462 465 In [5]: obj2 = NoDoc()
463 466
464 467 In [6]: %pdoc obj2
465 468 No documentation found for obj2
466 469 """
467 470
468 471 head = self.__head # For convenience
469 472 lines = []
470 473 ds = getdoc(obj)
471 474 if formatter:
472 475 ds = formatter(ds).get('plain/text', ds)
473 476 if ds:
474 477 lines.append(head("Class docstring:"))
475 478 lines.append(indent(ds))
476 479 if inspect.isclass(obj) and hasattr(obj, '__init__'):
477 480 init_ds = getdoc(obj.__init__)
478 481 if init_ds is not None:
479 482 lines.append(head("Init docstring:"))
480 483 lines.append(indent(init_ds))
481 484 elif hasattr(obj,'__call__'):
482 485 call_ds = getdoc(obj.__call__)
483 486 if call_ds:
484 487 lines.append(head("Call docstring:"))
485 488 lines.append(indent(call_ds))
486 489
487 490 if not lines:
488 491 self.noinfo('documentation',oname)
489 492 else:
490 493 page.page('\n'.join(lines))
491 494
492 495 def psource(self, obj, oname=''):
493 496 """Print the source code for an object."""
494 497
495 498 # Flush the source cache because inspect can return out-of-date source
496 499 linecache.checkcache()
497 500 try:
498 501 src = getsource(obj, oname=oname)
499 502 except Exception:
500 503 src = None
501 504
502 505 if src is None:
503 506 self.noinfo('source', oname)
504 507 else:
505 508 page.page(self.format(src))
506 509
507 510 def pfile(self, obj, oname=''):
508 511 """Show the whole file where an object was defined."""
509 512
510 513 lineno = find_source_lines(obj)
511 514 if lineno is None:
512 515 self.noinfo('file', oname)
513 516 return
514 517
515 518 ofile = find_file(obj)
516 519 # run contents of file through pager starting at line where the object
517 520 # is defined, as long as the file isn't binary and is actually on the
518 521 # filesystem.
519 522 if ofile.endswith(('.so', '.dll', '.pyd')):
520 523 print('File %r is binary, not printing.' % ofile)
521 524 elif not os.path.isfile(ofile):
522 525 print('File %r does not exist, not printing.' % ofile)
523 526 else:
524 527 # Print only text files, not extension binaries. Note that
525 528 # getsourcelines returns lineno with 1-offset and page() uses
526 529 # 0-offset, so we must adjust.
527 530 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
528 531
529 532
530 533 def _mime_format(self, text:str, formatter=None) -> dict:
531 534 """Return a mime bundle representation of the input text.
532 535
533 536 - if `formatter` is None, the returned mime bundle has
534 537 a ``text/plain`` field, with the input text.
535 538 a ``text/html`` field with a ``<pre>`` tag containing the input text.
536 539
537 540 - if ``formatter`` is not None, it must be a callable transforming the
538 541 input text into a mime bundle. Default values for ``text/plain`` and
539 542 ``text/html`` representations are the ones described above.
540 543
541 544 Note:
542 545
543 546 Formatters returning strings are supported but this behavior is deprecated.
544 547
545 548 """
546 549 defaults = {
547 550 "text/plain": text,
548 551 "text/html": f"<pre>{html.escape(text)}</pre>",
549 552 }
550 553
551 554 if formatter is None:
552 555 return defaults
553 556 else:
554 557 formatted = formatter(text)
555 558
556 559 if not isinstance(formatted, dict):
557 560 # Handle the deprecated behavior of a formatter returning
558 561 # a string instead of a mime bundle.
559 562 return {"text/plain": formatted, "text/html": f"<pre>{formatted}</pre>"}
560 563
561 564 else:
562 565 return dict(defaults, **formatted)
563 566
564 567
565 568 def format_mime(self, bundle):
566 569 """Format a mimebundle being created by _make_info_unformatted into a real mimebundle"""
567 570 # Format text/plain mimetype
568 571 if isinstance(bundle["text/plain"], (list, tuple)):
569 572 # bundle['text/plain'] is a list of (head, formatted body) pairs
570 573 lines = []
571 574 _len = max(len(h) for h, _ in bundle["text/plain"])
572 575
573 576 for head, body in bundle["text/plain"]:
574 577 body = body.strip("\n")
575 578 delim = "\n" if "\n" in body else " "
576 579 lines.append(
577 580 f"{self.__head(head+':')}{(_len - len(head))*' '}{delim}{body}"
578 581 )
579 582
580 583 bundle["text/plain"] = "\n".join(lines)
581 584
582 585 # Format the text/html mimetype
583 586 if isinstance(bundle["text/html"], (list, tuple)):
584 587 # bundle['text/html'] is a list of (head, formatted body) pairs
585 588 bundle["text/html"] = "\n".join(
586 589 (f"<h1>{head}</h1>\n{body}" for (head, body) in bundle["text/html"])
587 590 )
588 591 return bundle
589 592
590 593 def _append_info_field(
591 594 self, bundle, title: str, key: str, info, omit_sections, formatter
592 595 ):
593 596 """Append an info value to the unformatted mimebundle being constructed by _make_info_unformatted"""
594 597 if title in omit_sections or key in omit_sections:
595 598 return
596 599 field = info[key]
597 600 if field is not None:
598 601 formatted_field = self._mime_format(field, formatter)
599 602 bundle["text/plain"].append((title, formatted_field["text/plain"]))
600 603 bundle["text/html"].append((title, formatted_field["text/html"]))
601 604
602 605 def _make_info_unformatted(self, obj, info, formatter, detail_level, omit_sections):
603 606 """Assemble the mimebundle as unformatted lists of information"""
604 607 bundle = {
605 608 "text/plain": [],
606 609 "text/html": [],
607 610 }
608 611
609 612 # A convenience function to simplify calls below
610 613 def append_field(bundle, title: str, key: str, formatter=None):
611 614 self._append_info_field(
612 615 bundle,
613 616 title=title,
614 617 key=key,
615 618 info=info,
616 619 omit_sections=omit_sections,
617 620 formatter=formatter,
618 621 )
619 622
620 623 def code_formatter(text):
621 624 return {
622 625 'text/plain': self.format(text),
623 626 'text/html': pylight(text)
624 627 }
625 628
626 629 if info["isalias"]:
627 630 append_field(bundle, "Repr", "string_form")
628 631
629 632 elif info['ismagic']:
630 633 if detail_level > 0:
631 634 append_field(bundle, "Source", "source", code_formatter)
632 635 else:
633 636 append_field(bundle, "Docstring", "docstring", formatter)
634 637 append_field(bundle, "File", "file")
635 638
636 639 elif info['isclass'] or is_simple_callable(obj):
637 640 # Functions, methods, classes
638 641 append_field(bundle, "Signature", "definition", code_formatter)
639 642 append_field(bundle, "Init signature", "init_definition", code_formatter)
640 643 append_field(bundle, "Docstring", "docstring", formatter)
641 644 if detail_level > 0 and info["source"]:
642 645 append_field(bundle, "Source", "source", code_formatter)
643 646 else:
644 647 append_field(bundle, "Init docstring", "init_docstring", formatter)
645 648
646 649 append_field(bundle, "File", "file")
647 650 append_field(bundle, "Type", "type_name")
648 651 append_field(bundle, "Subclasses", "subclasses")
649 652
650 653 else:
651 654 # General Python objects
652 655 append_field(bundle, "Signature", "definition", code_formatter)
653 656 append_field(bundle, "Call signature", "call_def", code_formatter)
654 657 append_field(bundle, "Type", "type_name")
655 658 append_field(bundle, "String form", "string_form")
656 659
657 660 # Namespace
658 661 if info["namespace"] != "Interactive":
659 662 append_field(bundle, "Namespace", "namespace")
660 663
661 664 append_field(bundle, "Length", "length")
662 665 append_field(bundle, "File", "file")
663 666
664 667 # Source or docstring, depending on detail level and whether
665 668 # source found.
666 669 if detail_level > 0 and info["source"]:
667 670 append_field(bundle, "Source", "source", code_formatter)
668 671 else:
669 672 append_field(bundle, "Docstring", "docstring", formatter)
670 673
671 674 append_field(bundle, "Class docstring", "class_docstring", formatter)
672 675 append_field(bundle, "Init docstring", "init_docstring", formatter)
673 676 append_field(bundle, "Call docstring", "call_docstring", formatter)
674 677 return bundle
675 678
676 679
677 680 def _get_info(
678 681 self, obj, oname="", formatter=None, info=None, detail_level=0, omit_sections=()
679 682 ):
680 683 """Retrieve an info dict and format it.
681 684
682 685 Parameters
683 686 ----------
684 687 obj : any
685 688 Object to inspect and return info from
686 689 oname : str (default: ''):
687 690 Name of the variable pointing to `obj`.
688 691 formatter : callable
689 692 info
690 693 already computed information
691 694 detail_level : integer
692 695 Granularity of detail level, if set to 1, give more information.
693 696 omit_sections : container[str]
694 697 Titles or keys to omit from output (can be set, tuple, etc., anything supporting `in`)
695 698 """
696 699
697 700 info = self.info(obj, oname=oname, info=info, detail_level=detail_level)
698 701 bundle = self._make_info_unformatted(
699 702 obj, info, formatter, detail_level=detail_level, omit_sections=omit_sections
700 703 )
701 704 return self.format_mime(bundle)
702 705
703 706 def pinfo(
704 707 self,
705 708 obj,
706 709 oname="",
707 710 formatter=None,
708 711 info=None,
709 712 detail_level=0,
710 713 enable_html_pager=True,
711 714 omit_sections=(),
712 715 ):
713 716 """Show detailed information about an object.
714 717
715 718 Optional arguments:
716 719
717 720 - oname: name of the variable pointing to the object.
718 721
719 722 - formatter: callable (optional)
720 723 A special formatter for docstrings.
721 724
722 725 The formatter is a callable that takes a string as an input
723 726 and returns either a formatted string or a mime type bundle
724 727 in the form of a dictionary.
725 728
726 729 Although the support of custom formatter returning a string
727 730 instead of a mime type bundle is deprecated.
728 731
729 732 - info: a structure with some information fields which may have been
730 733 precomputed already.
731 734
732 735 - detail_level: if set to 1, more information is given.
733 736
734 737 - omit_sections: set of section keys and titles to omit
735 738 """
736 739 info = self._get_info(
737 740 obj, oname, formatter, info, detail_level, omit_sections=omit_sections
738 741 )
739 742 if not enable_html_pager:
740 743 del info['text/html']
741 744 page.page(info)
742 745
743 746 def _info(self, obj, oname="", info=None, detail_level=0):
744 747 """
745 748 Inspector.info() was likely improperly marked as deprecated
746 749 while only a parameter was deprecated. We "un-deprecate" it.
747 750 """
748 751
749 752 warnings.warn(
750 753 "The `Inspector.info()` method has been un-deprecated as of 8.0 "
751 754 "and the `formatter=` keyword removed. `Inspector._info` is now "
752 755 "an alias, and you can just call `.info()` directly.",
753 756 DeprecationWarning,
754 757 stacklevel=2,
755 758 )
756 759 return self.info(obj, oname=oname, info=info, detail_level=detail_level)
757 760
758 761 def info(self, obj, oname="", info=None, detail_level=0) -> dict:
759 762 """Compute a dict with detailed information about an object.
760 763
761 764 Parameters
762 765 ----------
763 766 obj : any
764 767 An object to find information about
765 768 oname : str (default: '')
766 769 Name of the variable pointing to `obj`.
767 770 info : (default: None)
768 771 A struct (dict like with attr access) with some information fields
769 772 which may have been precomputed already.
770 773 detail_level : int (default:0)
771 774 If set to 1, more information is given.
772 775
773 776 Returns
774 777 -------
775 778 An object info dict with known fields from `info_fields`. Keys are
776 779 strings, values are string or None.
777 780 """
778 781
779 782 if info is None:
780 783 ismagic = False
781 784 isalias = False
782 785 ospace = ''
783 786 else:
784 787 ismagic = info.ismagic
785 788 isalias = info.isalias
786 789 ospace = info.namespace
787 790
788 791 # Get docstring, special-casing aliases:
789 792 if isalias:
790 793 if not callable(obj):
791 794 try:
792 795 ds = "Alias to the system command:\n %s" % obj[1]
793 796 except:
794 797 ds = "Alias: " + str(obj)
795 798 else:
796 799 ds = "Alias to " + str(obj)
797 800 if obj.__doc__:
798 801 ds += "\nDocstring:\n" + obj.__doc__
799 802 else:
800 ds = getdoc(obj)
801 if ds is None:
803 ds_or_None = getdoc(obj)
804 if ds_or_None is None:
802 805 ds = '<no docstring>'
806 else:
807 ds = ds_or_None
803 808
804 809 # store output in a dict, we initialize it here and fill it as we go
805 810 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None)
806 811
807 812 string_max = 200 # max size of strings to show (snipped if longer)
808 813 shalf = int((string_max - 5) / 2)
809 814
810 815 if ismagic:
811 816 out['type_name'] = 'Magic function'
812 817 elif isalias:
813 818 out['type_name'] = 'System alias'
814 819 else:
815 820 out['type_name'] = type(obj).__name__
816 821
817 822 try:
818 823 bclass = obj.__class__
819 824 out['base_class'] = str(bclass)
820 825 except:
821 826 pass
822 827
823 828 # String form, but snip if too long in ? form (full in ??)
824 829 if detail_level >= self.str_detail_level:
825 830 try:
826 831 ostr = str(obj)
827 832 str_head = 'string_form'
828 833 if not detail_level and len(ostr)>string_max:
829 834 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
830 835 ostr = ("\n" + " " * len(str_head.expandtabs())).\
831 836 join(q.strip() for q in ostr.split("\n"))
832 837 out[str_head] = ostr
833 838 except:
834 839 pass
835 840
836 841 if ospace:
837 842 out['namespace'] = ospace
838 843
839 844 # Length (for strings and lists)
840 845 try:
841 846 out['length'] = str(len(obj))
842 847 except Exception:
843 848 pass
844 849
845 850 # Filename where object was defined
846 851 binary_file = False
847 852 fname = find_file(obj)
848 853 if fname is None:
849 854 # if anything goes wrong, we don't want to show source, so it's as
850 855 # if the file was binary
851 856 binary_file = True
852 857 else:
853 858 if fname.endswith(('.so', '.dll', '.pyd')):
854 859 binary_file = True
855 860 elif fname.endswith('<string>'):
856 861 fname = 'Dynamically generated function. No source code available.'
857 862 out['file'] = compress_user(fname)
858 863
859 864 # Original source code for a callable, class or property.
860 865 if detail_level:
861 866 # Flush the source cache because inspect can return out-of-date
862 867 # source
863 868 linecache.checkcache()
864 869 try:
865 870 if isinstance(obj, property) or not binary_file:
866 871 src = getsource(obj, oname)
867 872 if src is not None:
868 873 src = src.rstrip()
869 874 out['source'] = src
870 875
871 876 except Exception:
872 877 pass
873 878
874 879 # Add docstring only if no source is to be shown (avoid repetitions).
875 880 if ds and not self._source_contains_docstring(out.get('source'), ds):
876 881 out['docstring'] = ds
877 882
878 883 # Constructor docstring for classes
879 884 if inspect.isclass(obj):
880 885 out['isclass'] = True
881 886
882 887 # get the init signature:
883 888 try:
884 889 init_def = self._getdef(obj, oname)
885 890 except AttributeError:
886 891 init_def = None
887 892
888 893 # get the __init__ docstring
889 894 try:
890 895 obj_init = obj.__init__
891 896 except AttributeError:
892 897 init_ds = None
893 898 else:
894 899 if init_def is None:
895 900 # Get signature from init if top-level sig failed.
896 901 # Can happen for built-in types (list, etc.).
897 902 try:
898 903 init_def = self._getdef(obj_init, oname)
899 904 except AttributeError:
900 905 pass
901 906 init_ds = getdoc(obj_init)
902 907 # Skip Python's auto-generated docstrings
903 908 if init_ds == _object_init_docstring:
904 909 init_ds = None
905 910
906 911 if init_def:
907 912 out['init_definition'] = init_def
908 913
909 914 if init_ds:
910 915 out['init_docstring'] = init_ds
911 916
912 917 names = [sub.__name__ for sub in type.__subclasses__(obj)]
913 918 if len(names) < 10:
914 919 all_names = ', '.join(names)
915 920 else:
916 921 all_names = ', '.join(names[:10]+['...'])
917 922 out['subclasses'] = all_names
918 923 # and class docstring for instances:
919 924 else:
920 925 # reconstruct the function definition and print it:
921 926 defln = self._getdef(obj, oname)
922 927 if defln:
923 928 out['definition'] = defln
924 929
925 930 # First, check whether the instance docstring is identical to the
926 931 # class one, and print it separately if they don't coincide. In
927 932 # most cases they will, but it's nice to print all the info for
928 933 # objects which use instance-customized docstrings.
929 934 if ds:
930 935 try:
931 936 cls = getattr(obj,'__class__')
932 937 except:
933 938 class_ds = None
934 939 else:
935 940 class_ds = getdoc(cls)
936 941 # Skip Python's auto-generated docstrings
937 942 if class_ds in _builtin_type_docstrings:
938 943 class_ds = None
939 944 if class_ds and ds != class_ds:
940 945 out['class_docstring'] = class_ds
941 946
942 947 # Next, try to show constructor docstrings
943 948 try:
944 949 init_ds = getdoc(obj.__init__)
945 950 # Skip Python's auto-generated docstrings
946 951 if init_ds == _object_init_docstring:
947 952 init_ds = None
948 953 except AttributeError:
949 954 init_ds = None
950 955 if init_ds:
951 956 out['init_docstring'] = init_ds
952 957
953 958 # Call form docstring for callable instances
954 959 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
955 960 call_def = self._getdef(obj.__call__, oname)
956 961 if call_def and (call_def != out.get('definition')):
957 962 # it may never be the case that call def and definition differ,
958 963 # but don't include the same signature twice
959 964 out['call_def'] = call_def
960 965 call_ds = getdoc(obj.__call__)
961 966 # Skip Python's auto-generated docstrings
962 967 if call_ds == _func_call_docstring:
963 968 call_ds = None
964 969 if call_ds:
965 970 out['call_docstring'] = call_ds
966 971
967 972 return object_info(**out)
968 973
969 974 @staticmethod
970 975 def _source_contains_docstring(src, doc):
971 976 """
972 977 Check whether the source *src* contains the docstring *doc*.
973 978
974 979 This is is helper function to skip displaying the docstring if the
975 980 source already contains it, avoiding repetition of information.
976 981 """
977 982 try:
978 983 def_node, = ast.parse(dedent(src)).body
979 984 return ast.get_docstring(def_node) == doc
980 985 except Exception:
981 986 # The source can become invalid or even non-existent (because it
982 987 # is re-fetched from the source file) so the above code fail in
983 988 # arbitrary ways.
984 989 return False
985 990
986 991 def psearch(self,pattern,ns_table,ns_search=[],
987 992 ignore_case=False,show_all=False, *, list_types=False):
988 993 """Search namespaces with wildcards for objects.
989 994
990 995 Arguments:
991 996
992 997 - pattern: string containing shell-like wildcards to use in namespace
993 998 searches and optionally a type specification to narrow the search to
994 999 objects of that type.
995 1000
996 1001 - ns_table: dict of name->namespaces for search.
997 1002
998 1003 Optional arguments:
999 1004
1000 1005 - ns_search: list of namespace names to include in search.
1001 1006
1002 1007 - ignore_case(False): make the search case-insensitive.
1003 1008
1004 1009 - show_all(False): show all names, including those starting with
1005 1010 underscores.
1006 1011
1007 1012 - list_types(False): list all available object types for object matching.
1008 1013 """
1009 1014 #print 'ps pattern:<%r>' % pattern # dbg
1010 1015
1011 1016 # defaults
1012 1017 type_pattern = 'all'
1013 1018 filter = ''
1014 1019
1015 1020 # list all object types
1016 1021 if list_types:
1017 1022 page.page('\n'.join(sorted(typestr2type)))
1018 1023 return
1019 1024
1020 1025 cmds = pattern.split()
1021 1026 len_cmds = len(cmds)
1022 1027 if len_cmds == 1:
1023 1028 # Only filter pattern given
1024 1029 filter = cmds[0]
1025 1030 elif len_cmds == 2:
1026 1031 # Both filter and type specified
1027 1032 filter,type_pattern = cmds
1028 1033 else:
1029 1034 raise ValueError('invalid argument string for psearch: <%s>' %
1030 1035 pattern)
1031 1036
1032 1037 # filter search namespaces
1033 1038 for name in ns_search:
1034 1039 if name not in ns_table:
1035 1040 raise ValueError('invalid namespace <%s>. Valid names: %s' %
1036 1041 (name,ns_table.keys()))
1037 1042
1038 1043 #print 'type_pattern:',type_pattern # dbg
1039 1044 search_result, namespaces_seen = set(), set()
1040 1045 for ns_name in ns_search:
1041 1046 ns = ns_table[ns_name]
1042 1047 # Normally, locals and globals are the same, so we just check one.
1043 1048 if id(ns) in namespaces_seen:
1044 1049 continue
1045 1050 namespaces_seen.add(id(ns))
1046 1051 tmp_res = list_namespace(ns, type_pattern, filter,
1047 1052 ignore_case=ignore_case, show_all=show_all)
1048 1053 search_result.update(tmp_res)
1049 1054
1050 1055 page.page('\n'.join(sorted(search_result)))
1051 1056
1052 1057
1053 1058 def _render_signature(obj_signature, obj_name) -> str:
1054 1059 """
1055 1060 This was mostly taken from inspect.Signature.__str__.
1056 1061 Look there for the comments.
1057 1062 The only change is to add linebreaks when this gets too long.
1058 1063 """
1059 1064 result = []
1060 1065 pos_only = False
1061 1066 kw_only = True
1062 1067 for param in obj_signature.parameters.values():
1063 if param.kind == inspect._POSITIONAL_ONLY:
1068 if param.kind == inspect.Parameter.POSITIONAL_ONLY:
1064 1069 pos_only = True
1065 1070 elif pos_only:
1066 1071 result.append('/')
1067 1072 pos_only = False
1068 1073
1069 if param.kind == inspect._VAR_POSITIONAL:
1074 if param.kind == inspect.Parameter.VAR_POSITIONAL:
1070 1075 kw_only = False
1071 elif param.kind == inspect._KEYWORD_ONLY and kw_only:
1076 elif param.kind == inspect.Parameter.KEYWORD_ONLY and kw_only:
1072 1077 result.append('*')
1073 1078 kw_only = False
1074 1079
1075 1080 result.append(str(param))
1076 1081
1077 1082 if pos_only:
1078 1083 result.append('/')
1079 1084
1080 1085 # add up name, parameters, braces (2), and commas
1081 1086 if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
1082 1087 # This doesn’t fit behind “Signature: ” in an inspect window.
1083 1088 rendered = '{}(\n{})'.format(obj_name, ''.join(
1084 1089 ' {},\n'.format(r) for r in result)
1085 1090 )
1086 1091 else:
1087 1092 rendered = '{}({})'.format(obj_name, ', '.join(result))
1088 1093
1089 1094 if obj_signature.return_annotation is not inspect._empty:
1090 1095 anno = inspect.formatannotation(obj_signature.return_annotation)
1091 1096 rendered += ' -> {}'.format(anno)
1092 1097
1093 1098 return rendered
@@ -1,1200 +1,1200 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import asyncio
13 13 import ast
14 14 import os
15 15 import signal
16 16 import shutil
17 17 import sys
18 18 import tempfile
19 19 import unittest
20 20 import pytest
21 21 from unittest import mock
22 22
23 23 from os.path import join
24 24
25 25 from IPython.core.error import InputRejected
26 26 from IPython.core.inputtransformer import InputTransformer
27 27 from IPython.core import interactiveshell
28 28 from IPython.core.oinspect import OInfo
29 29 from IPython.testing.decorators import (
30 30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
31 31 )
32 32 from IPython.testing import tools as tt
33 33 from IPython.utils.process import find_cmd
34 34
35 35 #-----------------------------------------------------------------------------
36 36 # Globals
37 37 #-----------------------------------------------------------------------------
38 38 # This is used by every single test, no point repeating it ad nauseam
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Tests
42 42 #-----------------------------------------------------------------------------
43 43
44 44 class DerivedInterrupt(KeyboardInterrupt):
45 45 pass
46 46
47 47 class InteractiveShellTestCase(unittest.TestCase):
48 48 def test_naked_string_cells(self):
49 49 """Test that cells with only naked strings are fully executed"""
50 50 # First, single-line inputs
51 51 ip.run_cell('"a"\n')
52 52 self.assertEqual(ip.user_ns['_'], 'a')
53 53 # And also multi-line cells
54 54 ip.run_cell('"""a\nb"""\n')
55 55 self.assertEqual(ip.user_ns['_'], 'a\nb')
56 56
57 57 def test_run_empty_cell(self):
58 58 """Just make sure we don't get a horrible error with a blank
59 59 cell of input. Yes, I did overlook that."""
60 60 old_xc = ip.execution_count
61 61 res = ip.run_cell('')
62 62 self.assertEqual(ip.execution_count, old_xc)
63 63 self.assertEqual(res.execution_count, None)
64 64
65 65 def test_run_cell_multiline(self):
66 66 """Multi-block, multi-line cells must execute correctly.
67 67 """
68 68 src = '\n'.join(["x=1",
69 69 "y=2",
70 70 "if 1:",
71 71 " x += 1",
72 72 " y += 1",])
73 73 res = ip.run_cell(src)
74 74 self.assertEqual(ip.user_ns['x'], 2)
75 75 self.assertEqual(ip.user_ns['y'], 3)
76 76 self.assertEqual(res.success, True)
77 77 self.assertEqual(res.result, None)
78 78
79 79 def test_multiline_string_cells(self):
80 80 "Code sprinkled with multiline strings should execute (GH-306)"
81 81 ip.run_cell('tmp=0')
82 82 self.assertEqual(ip.user_ns['tmp'], 0)
83 83 res = ip.run_cell('tmp=1;"""a\nb"""\n')
84 84 self.assertEqual(ip.user_ns['tmp'], 1)
85 85 self.assertEqual(res.success, True)
86 86 self.assertEqual(res.result, "a\nb")
87 87
88 88 def test_dont_cache_with_semicolon(self):
89 89 "Ending a line with semicolon should not cache the returned object (GH-307)"
90 90 oldlen = len(ip.user_ns['Out'])
91 91 for cell in ['1;', '1;1;']:
92 92 res = ip.run_cell(cell, store_history=True)
93 93 newlen = len(ip.user_ns['Out'])
94 94 self.assertEqual(oldlen, newlen)
95 95 self.assertIsNone(res.result)
96 96 i = 0
97 97 #also test the default caching behavior
98 98 for cell in ['1', '1;1']:
99 99 ip.run_cell(cell, store_history=True)
100 100 newlen = len(ip.user_ns['Out'])
101 101 i += 1
102 102 self.assertEqual(oldlen+i, newlen)
103 103
104 104 def test_syntax_error(self):
105 105 res = ip.run_cell("raise = 3")
106 106 self.assertIsInstance(res.error_before_exec, SyntaxError)
107 107
108 108 def test_open_standard_input_stream(self):
109 109 res = ip.run_cell("open(0)")
110 110 self.assertIsInstance(res.error_in_exec, ValueError)
111 111
112 112 def test_open_standard_output_stream(self):
113 113 res = ip.run_cell("open(1)")
114 114 self.assertIsInstance(res.error_in_exec, ValueError)
115 115
116 116 def test_open_standard_error_stream(self):
117 117 res = ip.run_cell("open(2)")
118 118 self.assertIsInstance(res.error_in_exec, ValueError)
119 119
120 120 def test_In_variable(self):
121 121 "Verify that In variable grows with user input (GH-284)"
122 122 oldlen = len(ip.user_ns['In'])
123 123 ip.run_cell('1;', store_history=True)
124 124 newlen = len(ip.user_ns['In'])
125 125 self.assertEqual(oldlen+1, newlen)
126 126 self.assertEqual(ip.user_ns['In'][-1],'1;')
127 127
128 128 def test_magic_names_in_string(self):
129 129 ip.run_cell('a = """\n%exit\n"""')
130 130 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
131 131
132 132 def test_trailing_newline(self):
133 133 """test that running !(command) does not raise a SyntaxError"""
134 134 ip.run_cell('!(true)\n', False)
135 135 ip.run_cell('!(true)\n\n\n', False)
136 136
137 137 def test_gh_597(self):
138 138 """Pretty-printing lists of objects with non-ascii reprs may cause
139 139 problems."""
140 140 class Spam(object):
141 141 def __repr__(self):
142 142 return "\xe9"*50
143 143 import IPython.core.formatters
144 144 f = IPython.core.formatters.PlainTextFormatter()
145 145 f([Spam(),Spam()])
146 146
147 147
148 148 def test_future_flags(self):
149 149 """Check that future flags are used for parsing code (gh-777)"""
150 150 ip.run_cell('from __future__ import barry_as_FLUFL')
151 151 try:
152 152 ip.run_cell('prfunc_return_val = 1 <> 2')
153 153 assert 'prfunc_return_val' in ip.user_ns
154 154 finally:
155 155 # Reset compiler flags so we don't mess up other tests.
156 156 ip.compile.reset_compiler_flags()
157 157
158 158 def test_can_pickle(self):
159 159 "Can we pickle objects defined interactively (GH-29)"
160 160 ip = get_ipython()
161 161 ip.reset()
162 162 ip.run_cell(("class Mylist(list):\n"
163 163 " def __init__(self,x=[]):\n"
164 164 " list.__init__(self,x)"))
165 165 ip.run_cell("w=Mylist([1,2,3])")
166 166
167 167 from pickle import dumps
168 168
169 169 # We need to swap in our main module - this is only necessary
170 170 # inside the test framework, because IPython puts the interactive module
171 171 # in place (but the test framework undoes this).
172 172 _main = sys.modules['__main__']
173 173 sys.modules['__main__'] = ip.user_module
174 174 try:
175 175 res = dumps(ip.user_ns["w"])
176 176 finally:
177 177 sys.modules['__main__'] = _main
178 178 self.assertTrue(isinstance(res, bytes))
179 179
180 180 def test_global_ns(self):
181 181 "Code in functions must be able to access variables outside them."
182 182 ip = get_ipython()
183 183 ip.run_cell("a = 10")
184 184 ip.run_cell(("def f(x):\n"
185 185 " return x + a"))
186 186 ip.run_cell("b = f(12)")
187 187 self.assertEqual(ip.user_ns["b"], 22)
188 188
189 189 def test_bad_custom_tb(self):
190 190 """Check that InteractiveShell is protected from bad custom exception handlers"""
191 191 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
192 192 self.assertEqual(ip.custom_exceptions, (IOError,))
193 193 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
194 194 ip.run_cell(u'raise IOError("foo")')
195 195 self.assertEqual(ip.custom_exceptions, ())
196 196
197 197 def test_bad_custom_tb_return(self):
198 198 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
199 199 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
200 200 self.assertEqual(ip.custom_exceptions, (NameError,))
201 201 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
202 202 ip.run_cell(u'a=abracadabra')
203 203 self.assertEqual(ip.custom_exceptions, ())
204 204
205 205 def test_drop_by_id(self):
206 206 myvars = {"a":object(), "b":object(), "c": object()}
207 207 ip.push(myvars, interactive=False)
208 208 for name in myvars:
209 209 assert name in ip.user_ns, name
210 210 assert name in ip.user_ns_hidden, name
211 211 ip.user_ns['b'] = 12
212 212 ip.drop_by_id(myvars)
213 213 for name in ["a", "c"]:
214 214 assert name not in ip.user_ns, name
215 215 assert name not in ip.user_ns_hidden, name
216 216 assert ip.user_ns['b'] == 12
217 217 ip.reset()
218 218
219 219 def test_var_expand(self):
220 220 ip.user_ns['f'] = u'Ca\xf1o'
221 221 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
222 222 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
223 223 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
224 224 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
225 225
226 226 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
227 227
228 228 ip.user_ns['f'] = b'Ca\xc3\xb1o'
229 229 # This should not raise any exception:
230 230 ip.var_expand(u'echo $f')
231 231
232 232 def test_var_expand_local(self):
233 233 """Test local variable expansion in !system and %magic calls"""
234 234 # !system
235 235 ip.run_cell(
236 236 "def test():\n"
237 237 ' lvar = "ttt"\n'
238 238 " ret = !echo {lvar}\n"
239 239 " return ret[0]\n"
240 240 )
241 241 res = ip.user_ns["test"]()
242 242 self.assertIn("ttt", res)
243 243
244 244 # %magic
245 245 ip.run_cell(
246 246 "def makemacro():\n"
247 247 ' macroname = "macro_var_expand_locals"\n'
248 248 " %macro {macroname} codestr\n"
249 249 )
250 250 ip.user_ns["codestr"] = "str(12)"
251 251 ip.run_cell("makemacro()")
252 252 self.assertIn("macro_var_expand_locals", ip.user_ns)
253 253
254 254 def test_var_expand_self(self):
255 255 """Test variable expansion with the name 'self', which was failing.
256 256
257 257 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
258 258 """
259 259 ip.run_cell(
260 260 "class cTest:\n"
261 261 ' classvar="see me"\n'
262 262 " def test(self):\n"
263 263 " res = !echo Variable: {self.classvar}\n"
264 264 " return res[0]\n"
265 265 )
266 266 self.assertIn("see me", ip.user_ns["cTest"]().test())
267 267
268 268 def test_bad_var_expand(self):
269 269 """var_expand on invalid formats shouldn't raise"""
270 270 # SyntaxError
271 271 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
272 272 # NameError
273 273 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
274 274 # ZeroDivisionError
275 275 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
276 276
277 277 def test_silent_postexec(self):
278 278 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
279 279 pre_explicit = mock.Mock()
280 280 pre_always = mock.Mock()
281 281 post_explicit = mock.Mock()
282 282 post_always = mock.Mock()
283 283 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
284 284
285 285 ip.events.register('pre_run_cell', pre_explicit)
286 286 ip.events.register('pre_execute', pre_always)
287 287 ip.events.register('post_run_cell', post_explicit)
288 288 ip.events.register('post_execute', post_always)
289 289
290 290 try:
291 291 ip.run_cell("1", silent=True)
292 292 assert pre_always.called
293 293 assert not pre_explicit.called
294 294 assert post_always.called
295 295 assert not post_explicit.called
296 296 # double-check that non-silent exec did what we expected
297 297 # silent to avoid
298 298 ip.run_cell("1")
299 299 assert pre_explicit.called
300 300 assert post_explicit.called
301 301 info, = pre_explicit.call_args[0]
302 302 result, = post_explicit.call_args[0]
303 303 self.assertEqual(info, result.info)
304 304 # check that post hooks are always called
305 305 [m.reset_mock() for m in all_mocks]
306 306 ip.run_cell("syntax error")
307 307 assert pre_always.called
308 308 assert pre_explicit.called
309 309 assert post_always.called
310 310 assert post_explicit.called
311 311 info, = pre_explicit.call_args[0]
312 312 result, = post_explicit.call_args[0]
313 313 self.assertEqual(info, result.info)
314 314 finally:
315 315 # remove post-exec
316 316 ip.events.unregister('pre_run_cell', pre_explicit)
317 317 ip.events.unregister('pre_execute', pre_always)
318 318 ip.events.unregister('post_run_cell', post_explicit)
319 319 ip.events.unregister('post_execute', post_always)
320 320
321 321 def test_silent_noadvance(self):
322 322 """run_cell(silent=True) doesn't advance execution_count"""
323 323 ec = ip.execution_count
324 324 # silent should force store_history=False
325 325 ip.run_cell("1", store_history=True, silent=True)
326 326
327 327 self.assertEqual(ec, ip.execution_count)
328 328 # double-check that non-silent exec did what we expected
329 329 # silent to avoid
330 330 ip.run_cell("1", store_history=True)
331 331 self.assertEqual(ec+1, ip.execution_count)
332 332
333 333 def test_silent_nodisplayhook(self):
334 334 """run_cell(silent=True) doesn't trigger displayhook"""
335 335 d = dict(called=False)
336 336
337 337 trap = ip.display_trap
338 338 save_hook = trap.hook
339 339
340 340 def failing_hook(*args, **kwargs):
341 341 d['called'] = True
342 342
343 343 try:
344 344 trap.hook = failing_hook
345 345 res = ip.run_cell("1", silent=True)
346 346 self.assertFalse(d['called'])
347 347 self.assertIsNone(res.result)
348 348 # double-check that non-silent exec did what we expected
349 349 # silent to avoid
350 350 ip.run_cell("1")
351 351 self.assertTrue(d['called'])
352 352 finally:
353 353 trap.hook = save_hook
354 354
355 355 def test_ofind_line_magic(self):
356 356 from IPython.core.magic import register_line_magic
357 357
358 358 @register_line_magic
359 359 def lmagic(line):
360 360 "A line magic"
361 361
362 362 # Get info on line magic
363 363 lfind = ip._ofind("lmagic")
364 364 info = OInfo(
365 365 found=True,
366 366 isalias=False,
367 367 ismagic=True,
368 368 namespace="IPython internal",
369 369 obj=lmagic,
370 370 parent=None,
371 371 )
372 372 self.assertEqual(lfind, info)
373 373
374 374 def test_ofind_cell_magic(self):
375 375 from IPython.core.magic import register_cell_magic
376 376
377 377 @register_cell_magic
378 378 def cmagic(line, cell):
379 379 "A cell magic"
380 380
381 381 # Get info on cell magic
382 382 find = ip._ofind("cmagic")
383 383 info = OInfo(
384 384 found=True,
385 385 isalias=False,
386 386 ismagic=True,
387 387 namespace="IPython internal",
388 388 obj=cmagic,
389 389 parent=None,
390 390 )
391 391 self.assertEqual(find, info)
392 392
393 393 def test_ofind_property_with_error(self):
394 394 class A(object):
395 395 @property
396 396 def foo(self):
397 397 raise NotImplementedError() # pragma: no cover
398 398
399 399 a = A()
400 400
401 401 found = ip._ofind("a.foo", [("locals", locals())])
402 402 info = OInfo(
403 403 found=True,
404 404 isalias=False,
405 405 ismagic=False,
406 406 namespace="locals",
407 407 obj=A.foo,
408 408 parent=a,
409 409 )
410 410 self.assertEqual(found, info)
411 411
412 412 def test_ofind_multiple_attribute_lookups(self):
413 413 class A(object):
414 414 @property
415 415 def foo(self):
416 416 raise NotImplementedError() # pragma: no cover
417 417
418 418 a = A()
419 419 a.a = A()
420 420 a.a.a = A()
421 421
422 422 found = ip._ofind("a.a.a.foo", [("locals", locals())])
423 423 info = OInfo(
424 424 found=True,
425 425 isalias=False,
426 426 ismagic=False,
427 427 namespace="locals",
428 428 obj=A.foo,
429 429 parent=a.a.a,
430 430 )
431 431 self.assertEqual(found, info)
432 432
433 433 def test_ofind_slotted_attributes(self):
434 434 class A(object):
435 435 __slots__ = ['foo']
436 436 def __init__(self):
437 437 self.foo = 'bar'
438 438
439 439 a = A()
440 440 found = ip._ofind("a.foo", [("locals", locals())])
441 441 info = OInfo(
442 442 found=True,
443 443 isalias=False,
444 444 ismagic=False,
445 445 namespace="locals",
446 446 obj=a.foo,
447 447 parent=a,
448 448 )
449 449 self.assertEqual(found, info)
450 450
451 451 found = ip._ofind("a.bar", [("locals", locals())])
452 info = OInfo(
452 expected = OInfo(
453 453 found=False,
454 454 isalias=False,
455 455 ismagic=False,
456 456 namespace=None,
457 457 obj=None,
458 458 parent=a,
459 459 )
460 self.assertEqual(found, info)
460 assert found == expected
461 461
462 462 def test_ofind_prefers_property_to_instance_level_attribute(self):
463 463 class A(object):
464 464 @property
465 465 def foo(self):
466 466 return 'bar'
467 467 a = A()
468 468 a.__dict__["foo"] = "baz"
469 469 self.assertEqual(a.foo, "bar")
470 470 found = ip._ofind("a.foo", [("locals", locals())])
471 471 self.assertIs(found.obj, A.foo)
472 472
473 473 def test_custom_syntaxerror_exception(self):
474 474 called = []
475 475 def my_handler(shell, etype, value, tb, tb_offset=None):
476 476 called.append(etype)
477 477 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
478 478
479 479 ip.set_custom_exc((SyntaxError,), my_handler)
480 480 try:
481 481 ip.run_cell("1f")
482 482 # Check that this was called, and only once.
483 483 self.assertEqual(called, [SyntaxError])
484 484 finally:
485 485 # Reset the custom exception hook
486 486 ip.set_custom_exc((), None)
487 487
488 488 def test_custom_exception(self):
489 489 called = []
490 490 def my_handler(shell, etype, value, tb, tb_offset=None):
491 491 called.append(etype)
492 492 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
493 493
494 494 ip.set_custom_exc((ValueError,), my_handler)
495 495 try:
496 496 res = ip.run_cell("raise ValueError('test')")
497 497 # Check that this was called, and only once.
498 498 self.assertEqual(called, [ValueError])
499 499 # Check that the error is on the result object
500 500 self.assertIsInstance(res.error_in_exec, ValueError)
501 501 finally:
502 502 # Reset the custom exception hook
503 503 ip.set_custom_exc((), None)
504 504
505 505 @mock.patch("builtins.print")
506 506 def test_showtraceback_with_surrogates(self, mocked_print):
507 507 values = []
508 508
509 509 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
510 510 values.append(value)
511 511 if value == chr(0xD8FF):
512 512 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
513 513
514 514 # mock builtins.print
515 515 mocked_print.side_effect = mock_print_func
516 516
517 517 # ip._showtraceback() is replaced in globalipapp.py.
518 518 # Call original method to test.
519 519 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
520 520
521 521 self.assertEqual(mocked_print.call_count, 2)
522 522 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
523 523
524 524 def test_mktempfile(self):
525 525 filename = ip.mktempfile()
526 526 # Check that we can open the file again on Windows
527 527 with open(filename, "w", encoding="utf-8") as f:
528 528 f.write("abc")
529 529
530 530 filename = ip.mktempfile(data="blah")
531 531 with open(filename, "r", encoding="utf-8") as f:
532 532 self.assertEqual(f.read(), "blah")
533 533
534 534 def test_new_main_mod(self):
535 535 # Smoketest to check that this accepts a unicode module name
536 536 name = u'jiefmw'
537 537 mod = ip.new_main_mod(u'%s.py' % name, name)
538 538 self.assertEqual(mod.__name__, name)
539 539
540 540 def test_get_exception_only(self):
541 541 try:
542 542 raise KeyboardInterrupt
543 543 except KeyboardInterrupt:
544 544 msg = ip.get_exception_only()
545 545 self.assertEqual(msg, 'KeyboardInterrupt\n')
546 546
547 547 try:
548 548 raise DerivedInterrupt("foo")
549 549 except KeyboardInterrupt:
550 550 msg = ip.get_exception_only()
551 551 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
552 552
553 553 def test_inspect_text(self):
554 554 ip.run_cell('a = 5')
555 555 text = ip.object_inspect_text('a')
556 556 self.assertIsInstance(text, str)
557 557
558 558 def test_last_execution_result(self):
559 559 """ Check that last execution result gets set correctly (GH-10702) """
560 560 result = ip.run_cell('a = 5; a')
561 561 self.assertTrue(ip.last_execution_succeeded)
562 562 self.assertEqual(ip.last_execution_result.result, 5)
563 563
564 564 result = ip.run_cell('a = x_invalid_id_x')
565 565 self.assertFalse(ip.last_execution_succeeded)
566 566 self.assertFalse(ip.last_execution_result.success)
567 567 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
568 568
569 569 def test_reset_aliasing(self):
570 570 """ Check that standard posix aliases work after %reset. """
571 571 if os.name != 'posix':
572 572 return
573 573
574 574 ip.reset()
575 575 for cmd in ('clear', 'more', 'less', 'man'):
576 576 res = ip.run_cell('%' + cmd)
577 577 self.assertEqual(res.success, True)
578 578
579 579
580 580 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
581 581
582 582 @onlyif_unicode_paths
583 583 def setUp(self):
584 584 self.BASETESTDIR = tempfile.mkdtemp()
585 585 self.TESTDIR = join(self.BASETESTDIR, u"åäö")
586 586 os.mkdir(self.TESTDIR)
587 587 with open(
588 588 join(self.TESTDIR, "åäötestscript.py"), "w", encoding="utf-8"
589 589 ) as sfile:
590 590 sfile.write("pass\n")
591 591 self.oldpath = os.getcwd()
592 592 os.chdir(self.TESTDIR)
593 593 self.fname = u"åäötestscript.py"
594 594
595 595 def tearDown(self):
596 596 os.chdir(self.oldpath)
597 597 shutil.rmtree(self.BASETESTDIR)
598 598
599 599 @onlyif_unicode_paths
600 600 def test_1(self):
601 601 """Test safe_execfile with non-ascii path
602 602 """
603 603 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
604 604
605 605 class ExitCodeChecks(tt.TempFileMixin):
606 606
607 607 def setUp(self):
608 608 self.system = ip.system_raw
609 609
610 610 def test_exit_code_ok(self):
611 611 self.system('exit 0')
612 612 self.assertEqual(ip.user_ns['_exit_code'], 0)
613 613
614 614 def test_exit_code_error(self):
615 615 self.system('exit 1')
616 616 self.assertEqual(ip.user_ns['_exit_code'], 1)
617 617
618 618 @skipif(not hasattr(signal, 'SIGALRM'))
619 619 def test_exit_code_signal(self):
620 620 self.mktmp("import signal, time\n"
621 621 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
622 622 "time.sleep(1)\n")
623 623 self.system("%s %s" % (sys.executable, self.fname))
624 624 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
625 625
626 626 @onlyif_cmds_exist("csh")
627 627 def test_exit_code_signal_csh(self): # pragma: no cover
628 628 SHELL = os.environ.get("SHELL", None)
629 629 os.environ["SHELL"] = find_cmd("csh")
630 630 try:
631 631 self.test_exit_code_signal()
632 632 finally:
633 633 if SHELL is not None:
634 634 os.environ['SHELL'] = SHELL
635 635 else:
636 636 del os.environ['SHELL']
637 637
638 638
639 639 class TestSystemRaw(ExitCodeChecks):
640 640
641 641 def setUp(self):
642 642 super().setUp()
643 643 self.system = ip.system_raw
644 644
645 645 @onlyif_unicode_paths
646 646 def test_1(self):
647 647 """Test system_raw with non-ascii cmd
648 648 """
649 649 cmd = u'''python -c "'åäö'" '''
650 650 ip.system_raw(cmd)
651 651
652 652 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
653 653 @mock.patch('os.system', side_effect=KeyboardInterrupt)
654 654 def test_control_c(self, *mocks):
655 655 try:
656 656 self.system("sleep 1 # wont happen")
657 657 except KeyboardInterrupt: # pragma: no cove
658 658 self.fail(
659 659 "system call should intercept "
660 660 "keyboard interrupt from subprocess.call"
661 661 )
662 662 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
663 663
664 664
665 665 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
666 666 def test_magic_warnings(magic_cmd):
667 667 if sys.platform == "win32":
668 668 to_mock = "os.system"
669 669 expected_arg, expected_kwargs = magic_cmd, dict()
670 670 else:
671 671 to_mock = "subprocess.call"
672 672 expected_arg, expected_kwargs = magic_cmd, dict(
673 673 shell=True, executable=os.environ.get("SHELL", None)
674 674 )
675 675
676 676 with mock.patch(to_mock, return_value=0) as mock_sub:
677 677 with pytest.warns(Warning, match=r"You executed the system command"):
678 678 ip.system_raw(magic_cmd)
679 679 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
680 680
681 681
682 682 # TODO: Exit codes are currently ignored on Windows.
683 683 class TestSystemPipedExitCode(ExitCodeChecks):
684 684
685 685 def setUp(self):
686 686 super().setUp()
687 687 self.system = ip.system_piped
688 688
689 689 @skip_win32
690 690 def test_exit_code_ok(self):
691 691 ExitCodeChecks.test_exit_code_ok(self)
692 692
693 693 @skip_win32
694 694 def test_exit_code_error(self):
695 695 ExitCodeChecks.test_exit_code_error(self)
696 696
697 697 @skip_win32
698 698 def test_exit_code_signal(self):
699 699 ExitCodeChecks.test_exit_code_signal(self)
700 700
701 701 class TestModules(tt.TempFileMixin):
702 702 def test_extraneous_loads(self):
703 703 """Test we're not loading modules on startup that we shouldn't.
704 704 """
705 705 self.mktmp("import sys\n"
706 706 "print('numpy' in sys.modules)\n"
707 707 "print('ipyparallel' in sys.modules)\n"
708 708 "print('ipykernel' in sys.modules)\n"
709 709 )
710 710 out = "False\nFalse\nFalse\n"
711 711 tt.ipexec_validate(self.fname, out)
712 712
713 713 class Negator(ast.NodeTransformer):
714 714 """Negates all number literals in an AST."""
715 715
716 716 # for python 3.7 and earlier
717 717 def visit_Num(self, node):
718 718 node.n = -node.n
719 719 return node
720 720
721 721 # for python 3.8+
722 722 def visit_Constant(self, node):
723 723 if isinstance(node.value, int):
724 724 return self.visit_Num(node)
725 725 return node
726 726
727 727 class TestAstTransform(unittest.TestCase):
728 728 def setUp(self):
729 729 self.negator = Negator()
730 730 ip.ast_transformers.append(self.negator)
731 731
732 732 def tearDown(self):
733 733 ip.ast_transformers.remove(self.negator)
734 734
735 735 def test_non_int_const(self):
736 736 with tt.AssertPrints("hello"):
737 737 ip.run_cell('print("hello")')
738 738
739 739 def test_run_cell(self):
740 740 with tt.AssertPrints("-34"):
741 741 ip.run_cell("print(12 + 22)")
742 742
743 743 # A named reference to a number shouldn't be transformed.
744 744 ip.user_ns["n"] = 55
745 745 with tt.AssertNotPrints("-55"):
746 746 ip.run_cell("print(n)")
747 747
748 748 def test_timeit(self):
749 749 called = set()
750 750 def f(x):
751 751 called.add(x)
752 752 ip.push({'f':f})
753 753
754 754 with tt.AssertPrints("std. dev. of"):
755 755 ip.run_line_magic("timeit", "-n1 f(1)")
756 756 self.assertEqual(called, {-1})
757 757 called.clear()
758 758
759 759 with tt.AssertPrints("std. dev. of"):
760 760 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
761 761 self.assertEqual(called, {-2, -3})
762 762
763 763 def test_time(self):
764 764 called = []
765 765 def f(x):
766 766 called.append(x)
767 767 ip.push({'f':f})
768 768
769 769 # Test with an expression
770 770 with tt.AssertPrints("Wall time: "):
771 771 ip.run_line_magic("time", "f(5+9)")
772 772 self.assertEqual(called, [-14])
773 773 called[:] = []
774 774
775 775 # Test with a statement (different code path)
776 776 with tt.AssertPrints("Wall time: "):
777 777 ip.run_line_magic("time", "a = f(-3 + -2)")
778 778 self.assertEqual(called, [5])
779 779
780 780 def test_macro(self):
781 781 ip.push({'a':10})
782 782 # The AST transformation makes this do a+=-1
783 783 ip.define_macro("amacro", "a+=1\nprint(a)")
784 784
785 785 with tt.AssertPrints("9"):
786 786 ip.run_cell("amacro")
787 787 with tt.AssertPrints("8"):
788 788 ip.run_cell("amacro")
789 789
790 790 class TestMiscTransform(unittest.TestCase):
791 791
792 792
793 793 def test_transform_only_once(self):
794 794 cleanup = 0
795 795 line_t = 0
796 796 def count_cleanup(lines):
797 797 nonlocal cleanup
798 798 cleanup += 1
799 799 return lines
800 800
801 801 def count_line_t(lines):
802 802 nonlocal line_t
803 803 line_t += 1
804 804 return lines
805 805
806 806 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
807 807 ip.input_transformer_manager.line_transforms.append(count_line_t)
808 808
809 809 ip.run_cell('1')
810 810
811 811 assert cleanup == 1
812 812 assert line_t == 1
813 813
814 814 class IntegerWrapper(ast.NodeTransformer):
815 815 """Wraps all integers in a call to Integer()"""
816 816
817 817 # for Python 3.7 and earlier
818 818
819 819 # for Python 3.7 and earlier
820 820 def visit_Num(self, node):
821 821 if isinstance(node.n, int):
822 822 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
823 823 args=[node], keywords=[])
824 824 return node
825 825
826 826 # For Python 3.8+
827 827 def visit_Constant(self, node):
828 828 if isinstance(node.value, int):
829 829 return self.visit_Num(node)
830 830 return node
831 831
832 832
833 833 class TestAstTransform2(unittest.TestCase):
834 834 def setUp(self):
835 835 self.intwrapper = IntegerWrapper()
836 836 ip.ast_transformers.append(self.intwrapper)
837 837
838 838 self.calls = []
839 839 def Integer(*args):
840 840 self.calls.append(args)
841 841 return args
842 842 ip.push({"Integer": Integer})
843 843
844 844 def tearDown(self):
845 845 ip.ast_transformers.remove(self.intwrapper)
846 846 del ip.user_ns['Integer']
847 847
848 848 def test_run_cell(self):
849 849 ip.run_cell("n = 2")
850 850 self.assertEqual(self.calls, [(2,)])
851 851
852 852 # This shouldn't throw an error
853 853 ip.run_cell("o = 2.0")
854 854 self.assertEqual(ip.user_ns['o'], 2.0)
855 855
856 856 def test_run_cell_non_int(self):
857 857 ip.run_cell("n = 'a'")
858 858 assert self.calls == []
859 859
860 860 def test_timeit(self):
861 861 called = set()
862 862 def f(x):
863 863 called.add(x)
864 864 ip.push({'f':f})
865 865
866 866 with tt.AssertPrints("std. dev. of"):
867 867 ip.run_line_magic("timeit", "-n1 f(1)")
868 868 self.assertEqual(called, {(1,)})
869 869 called.clear()
870 870
871 871 with tt.AssertPrints("std. dev. of"):
872 872 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
873 873 self.assertEqual(called, {(2,), (3,)})
874 874
875 875 class ErrorTransformer(ast.NodeTransformer):
876 876 """Throws an error when it sees a number."""
877 877
878 878 def visit_Constant(self, node):
879 879 if isinstance(node.value, int):
880 880 raise ValueError("test")
881 881 return node
882 882
883 883
884 884 class TestAstTransformError(unittest.TestCase):
885 885 def test_unregistering(self):
886 886 err_transformer = ErrorTransformer()
887 887 ip.ast_transformers.append(err_transformer)
888 888
889 889 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
890 890 ip.run_cell("1 + 2")
891 891
892 892 # This should have been removed.
893 893 self.assertNotIn(err_transformer, ip.ast_transformers)
894 894
895 895
896 896 class StringRejector(ast.NodeTransformer):
897 897 """Throws an InputRejected when it sees a string literal.
898 898
899 899 Used to verify that NodeTransformers can signal that a piece of code should
900 900 not be executed by throwing an InputRejected.
901 901 """
902 902
903 903 # 3.8 only
904 904 def visit_Constant(self, node):
905 905 if isinstance(node.value, str):
906 906 raise InputRejected("test")
907 907 return node
908 908
909 909
910 910 class TestAstTransformInputRejection(unittest.TestCase):
911 911
912 912 def setUp(self):
913 913 self.transformer = StringRejector()
914 914 ip.ast_transformers.append(self.transformer)
915 915
916 916 def tearDown(self):
917 917 ip.ast_transformers.remove(self.transformer)
918 918
919 919 def test_input_rejection(self):
920 920 """Check that NodeTransformers can reject input."""
921 921
922 922 expect_exception_tb = tt.AssertPrints("InputRejected: test")
923 923 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
924 924
925 925 # Run the same check twice to verify that the transformer is not
926 926 # disabled after raising.
927 927 with expect_exception_tb, expect_no_cell_output:
928 928 ip.run_cell("'unsafe'")
929 929
930 930 with expect_exception_tb, expect_no_cell_output:
931 931 res = ip.run_cell("'unsafe'")
932 932
933 933 self.assertIsInstance(res.error_before_exec, InputRejected)
934 934
935 935 def test__IPYTHON__():
936 936 # This shouldn't raise a NameError, that's all
937 937 __IPYTHON__
938 938
939 939
940 940 class DummyRepr(object):
941 941 def __repr__(self):
942 942 return "DummyRepr"
943 943
944 944 def _repr_html_(self):
945 945 return "<b>dummy</b>"
946 946
947 947 def _repr_javascript_(self):
948 948 return "console.log('hi');", {'key': 'value'}
949 949
950 950
951 951 def test_user_variables():
952 952 # enable all formatters
953 953 ip.display_formatter.active_types = ip.display_formatter.format_types
954 954
955 955 ip.user_ns['dummy'] = d = DummyRepr()
956 956 keys = {'dummy', 'doesnotexist'}
957 957 r = ip.user_expressions({ key:key for key in keys})
958 958
959 959 assert keys == set(r.keys())
960 960 dummy = r["dummy"]
961 961 assert {"status", "data", "metadata"} == set(dummy.keys())
962 962 assert dummy["status"] == "ok"
963 963 data = dummy["data"]
964 964 metadata = dummy["metadata"]
965 965 assert data.get("text/html") == d._repr_html_()
966 966 js, jsmd = d._repr_javascript_()
967 967 assert data.get("application/javascript") == js
968 968 assert metadata.get("application/javascript") == jsmd
969 969
970 970 dne = r["doesnotexist"]
971 971 assert dne["status"] == "error"
972 972 assert dne["ename"] == "NameError"
973 973
974 974 # back to text only
975 975 ip.display_formatter.active_types = ['text/plain']
976 976
977 977 def test_user_expression():
978 978 # enable all formatters
979 979 ip.display_formatter.active_types = ip.display_formatter.format_types
980 980 query = {
981 981 'a' : '1 + 2',
982 982 'b' : '1/0',
983 983 }
984 984 r = ip.user_expressions(query)
985 985 import pprint
986 986 pprint.pprint(r)
987 987 assert set(r.keys()) == set(query.keys())
988 988 a = r["a"]
989 989 assert {"status", "data", "metadata"} == set(a.keys())
990 990 assert a["status"] == "ok"
991 991 data = a["data"]
992 992 metadata = a["metadata"]
993 993 assert data.get("text/plain") == "3"
994 994
995 995 b = r["b"]
996 996 assert b["status"] == "error"
997 997 assert b["ename"] == "ZeroDivisionError"
998 998
999 999 # back to text only
1000 1000 ip.display_formatter.active_types = ['text/plain']
1001 1001
1002 1002
1003 1003 class TestSyntaxErrorTransformer(unittest.TestCase):
1004 1004 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1005 1005
1006 1006 @staticmethod
1007 1007 def transformer(lines):
1008 1008 for line in lines:
1009 1009 pos = line.find('syntaxerror')
1010 1010 if pos >= 0:
1011 1011 e = SyntaxError('input contains "syntaxerror"')
1012 1012 e.text = line
1013 1013 e.offset = pos + 1
1014 1014 raise e
1015 1015 return lines
1016 1016
1017 1017 def setUp(self):
1018 1018 ip.input_transformers_post.append(self.transformer)
1019 1019
1020 1020 def tearDown(self):
1021 1021 ip.input_transformers_post.remove(self.transformer)
1022 1022
1023 1023 def test_syntaxerror_input_transformer(self):
1024 1024 with tt.AssertPrints('1234'):
1025 1025 ip.run_cell('1234')
1026 1026 with tt.AssertPrints('SyntaxError: invalid syntax'):
1027 1027 ip.run_cell('1 2 3') # plain python syntax error
1028 1028 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1029 1029 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1030 1030 with tt.AssertPrints('3456'):
1031 1031 ip.run_cell('3456')
1032 1032
1033 1033
1034 1034 class TestWarningSuppression(unittest.TestCase):
1035 1035 def test_warning_suppression(self):
1036 1036 ip.run_cell("import warnings")
1037 1037 try:
1038 1038 with self.assertWarnsRegex(UserWarning, "asdf"):
1039 1039 ip.run_cell("warnings.warn('asdf')")
1040 1040 # Here's the real test -- if we run that again, we should get the
1041 1041 # warning again. Traditionally, each warning was only issued once per
1042 1042 # IPython session (approximately), even if the user typed in new and
1043 1043 # different code that should have also triggered the warning, leading
1044 1044 # to much confusion.
1045 1045 with self.assertWarnsRegex(UserWarning, "asdf"):
1046 1046 ip.run_cell("warnings.warn('asdf')")
1047 1047 finally:
1048 1048 ip.run_cell("del warnings")
1049 1049
1050 1050
1051 1051 def test_deprecation_warning(self):
1052 1052 ip.run_cell("""
1053 1053 import warnings
1054 1054 def wrn():
1055 1055 warnings.warn(
1056 1056 "I AM A WARNING",
1057 1057 DeprecationWarning
1058 1058 )
1059 1059 """)
1060 1060 try:
1061 1061 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1062 1062 ip.run_cell("wrn()")
1063 1063 finally:
1064 1064 ip.run_cell("del warnings")
1065 1065 ip.run_cell("del wrn")
1066 1066
1067 1067
1068 1068 class TestImportNoDeprecate(tt.TempFileMixin):
1069 1069
1070 1070 def setUp(self):
1071 1071 """Make a valid python temp file."""
1072 1072 self.mktmp("""
1073 1073 import warnings
1074 1074 def wrn():
1075 1075 warnings.warn(
1076 1076 "I AM A WARNING",
1077 1077 DeprecationWarning
1078 1078 )
1079 1079 """)
1080 1080 super().setUp()
1081 1081
1082 1082 def test_no_dep(self):
1083 1083 """
1084 1084 No deprecation warning should be raised from imported functions
1085 1085 """
1086 1086 ip.run_cell("from {} import wrn".format(self.fname))
1087 1087
1088 1088 with tt.AssertNotPrints("I AM A WARNING"):
1089 1089 ip.run_cell("wrn()")
1090 1090 ip.run_cell("del wrn")
1091 1091
1092 1092
1093 1093 def test_custom_exc_count():
1094 1094 hook = mock.Mock(return_value=None)
1095 1095 ip.set_custom_exc((SyntaxError,), hook)
1096 1096 before = ip.execution_count
1097 1097 ip.run_cell("def foo()", store_history=True)
1098 1098 # restore default excepthook
1099 1099 ip.set_custom_exc((), None)
1100 1100 assert hook.call_count == 1
1101 1101 assert ip.execution_count == before + 1
1102 1102
1103 1103
1104 1104 def test_run_cell_async():
1105 1105 ip.run_cell("import asyncio")
1106 1106 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1107 1107 assert asyncio.iscoroutine(coro)
1108 1108 loop = asyncio.new_event_loop()
1109 1109 result = loop.run_until_complete(coro)
1110 1110 assert isinstance(result, interactiveshell.ExecutionResult)
1111 1111 assert result.result == 5
1112 1112
1113 1113
1114 1114 def test_run_cell_await():
1115 1115 ip.run_cell("import asyncio")
1116 1116 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1117 1117 assert ip.user_ns["_"] == 10
1118 1118
1119 1119
1120 1120 def test_run_cell_asyncio_run():
1121 1121 ip.run_cell("import asyncio")
1122 1122 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1123 1123 assert ip.user_ns["_"] == 1
1124 1124 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1125 1125 assert ip.user_ns["_"] == 2
1126 1126 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1127 1127 assert ip.user_ns["_"] == 3
1128 1128
1129 1129
1130 1130 def test_should_run_async():
1131 1131 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1132 1132 assert ip.should_run_async("await x", transformed_cell="await x")
1133 1133 assert ip.should_run_async(
1134 1134 "import asyncio; await asyncio.sleep(1)",
1135 1135 transformed_cell="import asyncio; await asyncio.sleep(1)",
1136 1136 )
1137 1137
1138 1138
1139 1139 def test_set_custom_completer():
1140 1140 num_completers = len(ip.Completer.matchers)
1141 1141
1142 1142 def foo(*args, **kwargs):
1143 1143 return "I'm a completer!"
1144 1144
1145 1145 ip.set_custom_completer(foo, 0)
1146 1146
1147 1147 # check that we've really added a new completer
1148 1148 assert len(ip.Completer.matchers) == num_completers + 1
1149 1149
1150 1150 # check that the first completer is the function we defined
1151 1151 assert ip.Completer.matchers[0]() == "I'm a completer!"
1152 1152
1153 1153 # clean up
1154 1154 ip.Completer.custom_matchers.pop()
1155 1155
1156 1156
1157 1157 class TestShowTracebackAttack(unittest.TestCase):
1158 1158 """Test that the interactive shell is resilient against the client attack of
1159 1159 manipulating the showtracebacks method. These attacks shouldn't result in an
1160 1160 unhandled exception in the kernel."""
1161 1161
1162 1162 def setUp(self):
1163 1163 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1164 1164
1165 1165 def tearDown(self):
1166 1166 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1167 1167
1168 1168 def test_set_show_tracebacks_none(self):
1169 1169 """Test the case of the client setting showtracebacks to None"""
1170 1170
1171 1171 result = ip.run_cell(
1172 1172 """
1173 1173 import IPython.core.interactiveshell
1174 1174 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1175 1175
1176 1176 assert False, "This should not raise an exception"
1177 1177 """
1178 1178 )
1179 1179 print(result)
1180 1180
1181 1181 assert result.result is None
1182 1182 assert isinstance(result.error_in_exec, TypeError)
1183 1183 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1184 1184
1185 1185 def test_set_show_tracebacks_noop(self):
1186 1186 """Test the case of the client setting showtracebacks to a no op lambda"""
1187 1187
1188 1188 result = ip.run_cell(
1189 1189 """
1190 1190 import IPython.core.interactiveshell
1191 1191 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1192 1192
1193 1193 assert False, "This should not raise an exception"
1194 1194 """
1195 1195 )
1196 1196 print(result)
1197 1197
1198 1198 assert result.result is None
1199 1199 assert isinstance(result.error_in_exec, AssertionError)
1200 1200 assert str(result.error_in_exec) == "This should not raise an exception"
@@ -1,1377 +1,1395 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Verbose and colourful traceback formatting.
4 4
5 5 **ColorTB**
6 6
7 7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 8 ColorTB class is a solution to that problem. It colors the different parts of a
9 9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 10 text editor.
11 11
12 12 Installation instructions for ColorTB::
13 13
14 14 import sys,ultratb
15 15 sys.excepthook = ultratb.ColorTB()
16 16
17 17 **VerboseTB**
18 18
19 19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 21 and intended it for CGI programmers, but why should they have all the fun? I
22 22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 23 but kind of neat, and maybe useful for long-running programs that you believe
24 24 are bug-free. If a crash *does* occur in that type of program you want details.
25 25 Give it a shot--you'll love it or you'll hate it.
26 26
27 27 .. note::
28 28
29 29 The Verbose mode prints the variables currently visible where the exception
30 30 happened (shortening their strings if too long). This can potentially be
31 31 very slow, if you happen to have a huge data structure whose string
32 32 representation is complex to compute. Your computer may appear to freeze for
33 33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 34 with Ctrl-C (maybe hitting it more than once).
35 35
36 36 If you encounter this kind of situation often, you may want to use the
37 37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 38 variables (but otherwise includes the information and context given by
39 39 Verbose).
40 40
41 41 .. note::
42 42
43 43 The verbose mode print all variables in the stack, which means it can
44 44 potentially leak sensitive information like access keys, or unencrypted
45 45 password.
46 46
47 47 Installation instructions for VerboseTB::
48 48
49 49 import sys,ultratb
50 50 sys.excepthook = ultratb.VerboseTB()
51 51
52 52 Note: Much of the code in this module was lifted verbatim from the standard
53 53 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
54 54
55 55 Color schemes
56 56 -------------
57 57
58 58 The colors are defined in the class TBTools through the use of the
59 59 ColorSchemeTable class. Currently the following exist:
60 60
61 61 - NoColor: allows all of this module to be used in any terminal (the color
62 62 escapes are just dummy blank strings).
63 63
64 64 - Linux: is meant to look good in a terminal like the Linux console (black
65 65 or very dark background).
66 66
67 67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 68 in light background terminals.
69 69
70 70 - Neutral: a neutral color scheme that should be readable on both light and
71 71 dark background
72 72
73 73 You can implement other color schemes easily, the syntax is fairly
74 74 self-explanatory. Please send back new schemes you develop to the author for
75 75 possible inclusion in future releases.
76 76
77 77 Inheritance diagram:
78 78
79 79 .. inheritance-diagram:: IPython.core.ultratb
80 80 :parts: 3
81 81 """
82 82
83 83 #*****************************************************************************
84 84 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
85 85 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
86 86 #
87 87 # Distributed under the terms of the BSD License. The full license is in
88 88 # the file COPYING, distributed as part of this software.
89 89 #*****************************************************************************
90 90
91 91
92 92 import inspect
93 93 import linecache
94 94 import pydoc
95 95 import sys
96 96 import time
97 97 import traceback
98 98 from types import TracebackType
99 99 from typing import Tuple, List, Any, Optional
100 100
101 101 import stack_data
102 102 from stack_data import FrameInfo as SDFrameInfo
103 103 from pygments.formatters.terminal256 import Terminal256Formatter
104 104 from pygments.styles import get_style_by_name
105 105
106 106 # IPython's own modules
107 107 from IPython import get_ipython
108 108 from IPython.core import debugger
109 109 from IPython.core.display_trap import DisplayTrap
110 110 from IPython.core.excolors import exception_colors
111 111 from IPython.utils import PyColorize
112 112 from IPython.utils import path as util_path
113 113 from IPython.utils import py3compat
114 114 from IPython.utils.terminal import get_terminal_size
115 115
116 116 import IPython.utils.colorable as colorable
117 117
118 118 # Globals
119 119 # amount of space to put line numbers before verbose tracebacks
120 120 INDENT_SIZE = 8
121 121
122 122 # Default color scheme. This is used, for example, by the traceback
123 123 # formatter. When running in an actual IPython instance, the user's rc.colors
124 124 # value is used, but having a module global makes this functionality available
125 125 # to users of ultratb who are NOT running inside ipython.
126 126 DEFAULT_SCHEME = 'NoColor'
127 127 FAST_THRESHOLD = 10_000
128 128
129 129 # ---------------------------------------------------------------------------
130 130 # Code begins
131 131
132 132 # Helper function -- largely belongs to VerboseTB, but we need the same
133 133 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
134 134 # can be recognized properly by ipython.el's py-traceback-line-re
135 135 # (SyntaxErrors have to be treated specially because they have no traceback)
136 136
137 137
138 138 def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
139 139 """
140 140 Format tracebacks lines with pointing arrow, leading numbers...
141 141
142 142 Parameters
143 143 ----------
144 144 lines : list[Line]
145 145 Colors
146 146 ColorScheme used.
147 147 lvals : str
148 148 Values of local variables, already colored, to inject just after the error line.
149 149 """
150 150 numbers_width = INDENT_SIZE - 1
151 151 res = []
152 152
153 153 for stack_line in lines:
154 154 if stack_line is stack_data.LINE_GAP:
155 155 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
156 156 continue
157 157
158 158 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
159 159 lineno = stack_line.lineno
160 160 if stack_line.is_current:
161 161 # This is the line with the error
162 162 pad = numbers_width - len(str(lineno))
163 163 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
164 164 start_color = Colors.linenoEm
165 165 else:
166 166 num = '%*s' % (numbers_width, lineno)
167 167 start_color = Colors.lineno
168 168
169 169 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
170 170
171 171 res.append(line)
172 172 if lvals and stack_line.is_current:
173 173 res.append(lvals + '\n')
174 174 return res
175 175
176 176 def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
177 177 """
178 178 Format tracebacks lines with pointing arrow, leading numbers...
179 179
180 180 Parameters
181 181 ==========
182 182
183 183 lnum: int
184 184 number of the target line of code.
185 185 index: int
186 186 which line in the list should be highlighted.
187 187 lines: list[string]
188 188 Colors:
189 189 ColorScheme used.
190 190 lvals: bytes
191 191 Values of local variables, already colored, to inject just after the error line.
192 192 _line_format: f (str) -> (str, bool)
193 193 return (colorized version of str, failure to do so)
194 194 """
195 195 numbers_width = INDENT_SIZE - 1
196 196 res = []
197 197
198 198 for i, line in enumerate(lines, lnum - index):
199 199 line = py3compat.cast_unicode(line)
200 200
201 201 new_line, err = _line_format(line, "str")
202 202 if not err:
203 203 line = new_line
204 204
205 205 if i == lnum:
206 206 # This is the line with the error
207 207 pad = numbers_width - len(str(i))
208 208 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
209 209 line = "%s%s%s %s%s" % (
210 210 Colors.linenoEm,
211 211 num,
212 212 Colors.line,
213 213 line,
214 214 Colors.Normal,
215 215 )
216 216 else:
217 217 num = "%*s" % (numbers_width, i)
218 218 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
219 219
220 220 res.append(line)
221 221 if lvals and i == lnum:
222 222 res.append(lvals + "\n")
223 223 return res
224 224
225 225
226 226 def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
227 227 """
228 228 Format filename lines with custom formatting from caching compiler or `File *.py` by default
229 229
230 230 Parameters
231 231 ----------
232 232 file : str
233 233 ColorFilename
234 234 ColorScheme's filename coloring to be used.
235 235 ColorNormal
236 236 ColorScheme's normal coloring to be used.
237 237 """
238 238 ipinst = get_ipython()
239 239 if (
240 240 ipinst is not None
241 241 and (data := ipinst.compile.format_code_name(file)) is not None
242 242 ):
243 243 label, name = data
244 244 if lineno is None:
245 245 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
246 246 else:
247 247 tpl_link = (
248 248 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
249 249 )
250 250 else:
251 251 label = "File"
252 252 name = util_path.compress_user(
253 253 py3compat.cast_unicode(file, util_path.fs_encoding)
254 254 )
255 255 if lineno is None:
256 256 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
257 257 else:
258 258 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
259 259 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
260 260
261 261 return tpl_link.format(label=label, name=name, lineno=lineno)
262 262
263 263 #---------------------------------------------------------------------------
264 264 # Module classes
265 265 class TBTools(colorable.Colorable):
266 266 """Basic tools used by all traceback printer classes."""
267 267
268 268 # Number of frames to skip when reporting tracebacks
269 269 tb_offset = 0
270 270
271 271 def __init__(
272 272 self,
273 273 color_scheme="NoColor",
274 274 call_pdb=False,
275 275 ostream=None,
276 276 parent=None,
277 277 config=None,
278 278 *,
279 279 debugger_cls=None,
280 280 ):
281 281 # Whether to call the interactive pdb debugger after printing
282 282 # tracebacks or not
283 283 super(TBTools, self).__init__(parent=parent, config=config)
284 284 self.call_pdb = call_pdb
285 285
286 286 # Output stream to write to. Note that we store the original value in
287 287 # a private attribute and then make the public ostream a property, so
288 288 # that we can delay accessing sys.stdout until runtime. The way
289 289 # things are written now, the sys.stdout object is dynamically managed
290 290 # so a reference to it should NEVER be stored statically. This
291 291 # property approach confines this detail to a single location, and all
292 292 # subclasses can simply access self.ostream for writing.
293 293 self._ostream = ostream
294 294
295 295 # Create color table
296 296 self.color_scheme_table = exception_colors()
297 297
298 298 self.set_colors(color_scheme)
299 299 self.old_scheme = color_scheme # save initial value for toggles
300 300 self.debugger_cls = debugger_cls or debugger.Pdb
301 301
302 302 if call_pdb:
303 303 self.pdb = self.debugger_cls()
304 304 else:
305 305 self.pdb = None
306 306
307 307 def _get_ostream(self):
308 308 """Output stream that exceptions are written to.
309 309
310 310 Valid values are:
311 311
312 312 - None: the default, which means that IPython will dynamically resolve
313 313 to sys.stdout. This ensures compatibility with most tools, including
314 314 Windows (where plain stdout doesn't recognize ANSI escapes).
315 315
316 316 - Any object with 'write' and 'flush' attributes.
317 317 """
318 318 return sys.stdout if self._ostream is None else self._ostream
319 319
320 320 def _set_ostream(self, val):
321 321 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
322 322 self._ostream = val
323 323
324 324 ostream = property(_get_ostream, _set_ostream)
325 325
326 326 @staticmethod
327 327 def _get_chained_exception(exception_value):
328 328 cause = getattr(exception_value, "__cause__", None)
329 329 if cause:
330 330 return cause
331 331 if getattr(exception_value, "__suppress_context__", False):
332 332 return None
333 333 return getattr(exception_value, "__context__", None)
334 334
335 335 def get_parts_of_chained_exception(
336 336 self, evalue
337 337 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
338 338 chained_evalue = self._get_chained_exception(evalue)
339 339
340 340 if chained_evalue:
341 341 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
342 342 return None
343 343
344 344 def prepare_chained_exception_message(self, cause) -> List[Any]:
345 345 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
346 346 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
347 347
348 348 if cause:
349 349 message = [[direct_cause]]
350 350 else:
351 351 message = [[exception_during_handling]]
352 352 return message
353 353
354 354 @property
355 355 def has_colors(self) -> bool:
356 356 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
357 357
358 358 def set_colors(self, *args, **kw):
359 359 """Shorthand access to the color table scheme selector method."""
360 360
361 361 # Set own color table
362 362 self.color_scheme_table.set_active_scheme(*args, **kw)
363 363 # for convenience, set Colors to the active scheme
364 364 self.Colors = self.color_scheme_table.active_colors
365 365 # Also set colors of debugger
366 366 if hasattr(self, 'pdb') and self.pdb is not None:
367 367 self.pdb.set_colors(*args, **kw)
368 368
369 369 def color_toggle(self):
370 370 """Toggle between the currently active color scheme and NoColor."""
371 371
372 372 if self.color_scheme_table.active_scheme_name == 'NoColor':
373 373 self.color_scheme_table.set_active_scheme(self.old_scheme)
374 374 self.Colors = self.color_scheme_table.active_colors
375 375 else:
376 376 self.old_scheme = self.color_scheme_table.active_scheme_name
377 377 self.color_scheme_table.set_active_scheme('NoColor')
378 378 self.Colors = self.color_scheme_table.active_colors
379 379
380 380 def stb2text(self, stb):
381 381 """Convert a structured traceback (a list) to a string."""
382 382 return '\n'.join(stb)
383 383
384 384 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
385 385 """Return formatted traceback.
386 386
387 387 Subclasses may override this if they add extra arguments.
388 388 """
389 389 tb_list = self.structured_traceback(etype, value, tb,
390 390 tb_offset, context)
391 391 return self.stb2text(tb_list)
392 392
393 393 def structured_traceback(
394 self, etype, evalue, tb, tb_offset: Optional[int] = None, context=5, mode=None
394 self,
395 etype: type,
396 evalue: Optional[BaseException],
397 etb: Optional[TracebackType] = None,
398 tb_offset: Optional[int] = None,
399 context=5,
395 400 ):
396 401 """Return a list of traceback frames.
397 402
398 403 Must be implemented by each class.
399 404 """
400 405 raise NotImplementedError()
401 406
402 407
403 408 #---------------------------------------------------------------------------
404 409 class ListTB(TBTools):
405 410 """Print traceback information from a traceback list, with optional color.
406 411
407 412 Calling requires 3 arguments: (etype, evalue, elist)
408 413 as would be obtained by::
409 414
410 415 etype, evalue, tb = sys.exc_info()
411 416 if tb:
412 417 elist = traceback.extract_tb(tb)
413 418 else:
414 419 elist = None
415 420
416 421 It can thus be used by programs which need to process the traceback before
417 422 printing (such as console replacements based on the code module from the
418 423 standard library).
419 424
420 425 Because they are meant to be called without a full traceback (only a
421 426 list), instances of this class can't call the interactive pdb debugger."""
422 427
423 428
424 429 def __call__(self, etype, value, elist):
425 430 self.ostream.flush()
426 431 self.ostream.write(self.text(etype, value, elist))
427 432 self.ostream.write('\n')
428 433
429 434 def _extract_tb(self, tb):
430 435 if tb:
431 436 return traceback.extract_tb(tb)
432 437 else:
433 438 return None
434 439
435 440 def structured_traceback(
436 441 self,
437 442 etype: type,
438 evalue: BaseException,
443 evalue: Optional[BaseException],
439 444 etb: Optional[TracebackType] = None,
440 445 tb_offset: Optional[int] = None,
441 446 context=5,
442 447 ):
443 448 """Return a color formatted string with the traceback info.
444 449
445 450 Parameters
446 451 ----------
447 452 etype : exception type
448 453 Type of the exception raised.
449 454 evalue : object
450 455 Data stored in the exception
451 456 etb : list | TracebackType | None
452 457 If list: List of frames, see class docstring for details.
453 458 If Traceback: Traceback of the exception.
454 459 tb_offset : int, optional
455 460 Number of frames in the traceback to skip. If not given, the
456 461 instance evalue is used (set in constructor).
457 462 context : int, optional
458 463 Number of lines of context information to print.
459 464
460 465 Returns
461 466 -------
462 467 String with formatted exception.
463 468 """
464 469 # This is a workaround to get chained_exc_ids in recursive calls
465 470 # etb should not be a tuple if structured_traceback is not recursive
466 471 if isinstance(etb, tuple):
467 472 etb, chained_exc_ids = etb
468 473 else:
469 474 chained_exc_ids = set()
470 475
471 476 if isinstance(etb, list):
472 477 elist = etb
473 478 elif etb is not None:
474 479 elist = self._extract_tb(etb)
475 480 else:
476 481 elist = []
477 482 tb_offset = self.tb_offset if tb_offset is None else tb_offset
478 483 assert isinstance(tb_offset, int)
479 484 Colors = self.Colors
480 485 out_list = []
481 486 if elist:
482 487
483 488 if tb_offset and len(elist) > tb_offset:
484 489 elist = elist[tb_offset:]
485 490
486 491 out_list.append('Traceback %s(most recent call last)%s:' %
487 492 (Colors.normalEm, Colors.Normal) + '\n')
488 493 out_list.extend(self._format_list(elist))
489 494 # The exception info should be a single entry in the list.
490 495 lines = ''.join(self._format_exception_only(etype, evalue))
491 496 out_list.append(lines)
492 497
493 498 exception = self.get_parts_of_chained_exception(evalue)
494 499
495 500 if exception and not id(exception[1]) in chained_exc_ids:
496 chained_exception_message = self.prepare_chained_exception_message(
497 evalue.__cause__)[0]
501 chained_exception_message = (
502 self.prepare_chained_exception_message(evalue.__cause__)[0]
503 if evalue is not None
504 else ""
505 )
498 506 etype, evalue, etb = exception
499 507 # Trace exception to avoid infinite 'cause' loop
500 508 chained_exc_ids.add(id(exception[1]))
501 509 chained_exceptions_tb_offset = 0
502 510 out_list = (
503 511 self.structured_traceback(
504 512 etype, evalue, (etb, chained_exc_ids),
505 513 chained_exceptions_tb_offset, context)
506 514 + chained_exception_message
507 515 + out_list)
508 516
509 517 return out_list
510 518
511 519 def _format_list(self, extracted_list):
512 520 """Format a list of traceback entry tuples for printing.
513 521
514 522 Given a list of tuples as returned by extract_tb() or
515 523 extract_stack(), return a list of strings ready for printing.
516 524 Each string in the resulting list corresponds to the item with the
517 525 same index in the argument list. Each string ends in a newline;
518 526 the strings may contain internal newlines as well, for those items
519 527 whose source text line is not None.
520 528
521 529 Lifted almost verbatim from traceback.py
522 530 """
523 531
524 532 Colors = self.Colors
525 533 list = []
526 534 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
527 535 normalCol, nameCol, fileCol, lineCol = (
528 536 # Emphasize the last entry
529 537 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
530 538 if ind == len(extracted_list) - 1
531 539 else (Colors.Normal, Colors.name, Colors.filename, "")
532 540 )
533 541
534 542 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
535 543 item = f"{normalCol} {fns}"
536 544
537 545 if name != "<module>":
538 546 item += f" in {nameCol}{name}{normalCol}\n"
539 547 else:
540 548 item += "\n"
541 549 if line:
542 550 item += f"{lineCol} {line.strip()}{normalCol}\n"
543 551 list.append(item)
544 552
545 553 return list
546 554
547 555 def _format_exception_only(self, etype, value):
548 556 """Format the exception part of a traceback.
549 557
550 558 The arguments are the exception type and value such as given by
551 559 sys.exc_info()[:2]. The return value is a list of strings, each ending
552 560 in a newline. Normally, the list contains a single string; however,
553 561 for SyntaxError exceptions, it contains several lines that (when
554 562 printed) display detailed information about where the syntax error
555 563 occurred. The message indicating which exception occurred is the
556 564 always last string in the list.
557 565
558 566 Also lifted nearly verbatim from traceback.py
559 567 """
560 568 have_filedata = False
561 569 Colors = self.Colors
562 570 list = []
563 571 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
564 572 if value is None:
565 573 # Not sure if this can still happen in Python 2.6 and above
566 574 list.append(stype + '\n')
567 575 else:
568 576 if issubclass(etype, SyntaxError):
569 577 have_filedata = True
570 578 if not value.filename: value.filename = "<string>"
571 579 if value.lineno:
572 580 lineno = value.lineno
573 581 textline = linecache.getline(value.filename, value.lineno)
574 582 else:
575 583 lineno = "unknown"
576 584 textline = ""
577 585 list.append(
578 586 "%s %s%s\n"
579 587 % (
580 588 Colors.normalEm,
581 589 _format_filename(
582 590 value.filename,
583 591 Colors.filenameEm,
584 592 Colors.normalEm,
585 593 lineno=(None if lineno == "unknown" else lineno),
586 594 ),
587 595 Colors.Normal,
588 596 )
589 597 )
590 598 if textline == "":
591 599 textline = py3compat.cast_unicode(value.text, "utf-8")
592 600
593 601 if textline is not None:
594 602 i = 0
595 603 while i < len(textline) and textline[i].isspace():
596 604 i += 1
597 605 list.append('%s %s%s\n' % (Colors.line,
598 606 textline.strip(),
599 607 Colors.Normal))
600 608 if value.offset is not None:
601 609 s = ' '
602 610 for c in textline[i:value.offset - 1]:
603 611 if c.isspace():
604 612 s += c
605 613 else:
606 614 s += ' '
607 615 list.append('%s%s^%s\n' % (Colors.caret, s,
608 616 Colors.Normal))
609 617
610 618 try:
611 619 s = value.msg
612 620 except Exception:
613 621 s = self._some_str(value)
614 622 if s:
615 623 list.append('%s%s:%s %s\n' % (stype, Colors.excName,
616 624 Colors.Normal, s))
617 625 else:
618 626 list.append('%s\n' % stype)
619 627
620 628 # sync with user hooks
621 629 if have_filedata:
622 630 ipinst = get_ipython()
623 631 if ipinst is not None:
624 632 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
625 633
626 634 return list
627 635
628 636 def get_exception_only(self, etype, value):
629 637 """Only print the exception type and message, without a traceback.
630 638
631 639 Parameters
632 640 ----------
633 641 etype : exception type
634 642 value : exception value
635 643 """
636 644 return ListTB.structured_traceback(self, etype, value)
637 645
638 646 def show_exception_only(self, etype, evalue):
639 647 """Only print the exception type and message, without a traceback.
640 648
641 649 Parameters
642 650 ----------
643 651 etype : exception type
644 652 evalue : exception value
645 653 """
646 654 # This method needs to use __call__ from *this* class, not the one from
647 655 # a subclass whose signature or behavior may be different
648 656 ostream = self.ostream
649 657 ostream.flush()
650 658 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
651 659 ostream.flush()
652 660
653 661 def _some_str(self, value):
654 662 # Lifted from traceback.py
655 663 try:
656 664 return py3compat.cast_unicode(str(value))
657 665 except:
658 666 return u'<unprintable %s object>' % type(value).__name__
659 667
660 668
661 669 class FrameInfo:
662 670 """
663 671 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
664 672 really long frames.
665 673 """
666 674
667 675 description: Optional[str]
668 676 filename: str
669 677 lineno: int
670 678
671 679 @classmethod
672 680 def _from_stack_data_FrameInfo(cls, frame_info):
673 681 return cls(
674 682 getattr(frame_info, "description", None),
675 683 getattr(frame_info, "filename", None),
676 684 getattr(frame_info, "lineno", None),
677 685 getattr(frame_info, "frame", None),
678 686 getattr(frame_info, "code", None),
679 687 sd=frame_info,
680 688 )
681 689
682 690 def __init__(self, description, filename, lineno, frame, code, sd=None):
683 691 self.description = description
684 692 self.filename = filename
685 693 self.lineno = lineno
686 694 self.frame = frame
687 695 self.code = code
688 696 self._sd = sd
689 697
690 698 # self.lines = []
691 699 if sd is None:
692 700 ix = inspect.getsourcelines(frame)
693 701 self.raw_lines = ix[0]
694 702
695 703 @property
696 704 def variables_in_executing_piece(self):
697 705 if self._sd:
698 706 return self._sd.variables_in_executing_piece
699 707 else:
700 708 return []
701 709
702 710 @property
703 711 def lines(self):
704 712 return self._sd.lines
705 713
706 714 @property
707 715 def executing(self):
708 716 if self._sd:
709 717 return self._sd.executing
710 718 else:
711 719 return None
712 720
713 721
714 722 # ----------------------------------------------------------------------------
715 723 class VerboseTB(TBTools):
716 724 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
717 725 of HTML. Requires inspect and pydoc. Crazy, man.
718 726
719 727 Modified version which optionally strips the topmost entries from the
720 728 traceback, to be used with alternate interpreters (because their own code
721 729 would appear in the traceback)."""
722 730
723 731 _tb_highlight = "bg:ansiyellow"
724 732
725 733 def __init__(
726 734 self,
727 735 color_scheme: str = "Linux",
728 736 call_pdb: bool = False,
729 737 ostream=None,
730 738 tb_offset: int = 0,
731 739 long_header: bool = False,
732 740 include_vars: bool = True,
733 741 check_cache=None,
734 742 debugger_cls=None,
735 743 parent=None,
736 744 config=None,
737 745 ):
738 746 """Specify traceback offset, headers and color scheme.
739 747
740 748 Define how many frames to drop from the tracebacks. Calling it with
741 749 tb_offset=1 allows use of this handler in interpreters which will have
742 750 their own code at the top of the traceback (VerboseTB will first
743 751 remove that frame before printing the traceback info)."""
744 752 TBTools.__init__(
745 753 self,
746 754 color_scheme=color_scheme,
747 755 call_pdb=call_pdb,
748 756 ostream=ostream,
749 757 parent=parent,
750 758 config=config,
751 759 debugger_cls=debugger_cls,
752 760 )
753 761 self.tb_offset = tb_offset
754 762 self.long_header = long_header
755 763 self.include_vars = include_vars
756 764 # By default we use linecache.checkcache, but the user can provide a
757 765 # different check_cache implementation. This was formerly used by the
758 766 # IPython kernel for interactive code, but is no longer necessary.
759 767 if check_cache is None:
760 768 check_cache = linecache.checkcache
761 769 self.check_cache = check_cache
762 770
763 771 self.skip_hidden = True
764 772
765 773 def format_record(self, frame_info: FrameInfo):
766 774 """Format a single stack frame"""
767 775 assert isinstance(frame_info, FrameInfo)
768 776 Colors = self.Colors # just a shorthand + quicker name lookup
769 777 ColorsNormal = Colors.Normal # used a lot
770 778
771 779 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
772 780 return ' %s[... skipping similar frames: %s]%s\n' % (
773 781 Colors.excName, frame_info.description, ColorsNormal)
774 782
775 783 indent = " " * INDENT_SIZE
776 784 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
777 785 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
778 786 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
779 787 Colors.vName,
780 788 Colors.valEm,
781 789 ColorsNormal,
782 790 )
783 791 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
784 792
785 793 link = _format_filename(
786 794 frame_info.filename,
787 795 Colors.filenameEm,
788 796 ColorsNormal,
789 797 lineno=frame_info.lineno,
790 798 )
791 799 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
792 800 if frame_info.executing is not None:
793 801 func = frame_info.executing.code_qualname()
794 802 else:
795 803 func = "?"
796 804 if func == "<module>":
797 805 call = ""
798 806 else:
799 807 # Decide whether to include variable details or not
800 808 var_repr = eqrepr if self.include_vars else nullrepr
801 809 try:
802 810 scope = inspect.formatargvalues(
803 811 args, varargs, varkw, locals_, formatvalue=var_repr
804 812 )
805 813 call = tpl_call.format(file=func, scope=scope)
806 814 except KeyError:
807 815 # This happens in situations like errors inside generator
808 816 # expressions, where local variables are listed in the
809 817 # line, but can't be extracted from the frame. I'm not
810 818 # 100% sure this isn't actually a bug in inspect itself,
811 819 # but since there's no info for us to compute with, the
812 820 # best we can do is report the failure and move on. Here
813 821 # we must *not* call any traceback construction again,
814 822 # because that would mess up use of %debug later on. So we
815 823 # simply report the failure and move on. The only
816 824 # limitation will be that this frame won't have locals
817 825 # listed in the call signature. Quite subtle problem...
818 826 # I can't think of a good way to validate this in a unit
819 827 # test, but running a script consisting of:
820 828 # dict( (k,v.strip()) for (k,v) in range(10) )
821 829 # will illustrate the error, if this exception catch is
822 830 # disabled.
823 831 call = tpl_call_fail % func
824 832
825 833 lvals = ''
826 834 lvals_list = []
827 835 if self.include_vars:
828 836 try:
829 837 # we likely want to fix stackdata at some point, but
830 838 # still need a workaround.
831 839 fibp = frame_info.variables_in_executing_piece
832 840 for var in fibp:
833 841 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
834 842 except Exception:
835 843 lvals_list.append(
836 844 "Exception trying to inspect frame. No more locals available."
837 845 )
838 846 if lvals_list:
839 847 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
840 848
841 849 result = f'{link}{", " if call else ""}{call}\n'
842 850 if frame_info._sd is None:
843 851 assert False
844 852 # fast fallback if file is too long
845 853 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
846 854 link = tpl_link % util_path.compress_user(frame_info.filename)
847 855 level = "%s %s\n" % (link, call)
848 856 _line_format = PyColorize.Parser(
849 857 style=self.color_scheme_table.active_scheme_name, parent=self
850 858 ).format2
851 859 first_line = frame_info.code.co_firstlineno
852 860 current_line = frame_info.lineno[0]
853 861 return "%s%s" % (
854 862 level,
855 863 "".join(
856 864 _simple_format_traceback_lines(
857 865 current_line,
858 866 current_line - first_line,
859 867 frame_info.raw_lines,
860 868 Colors,
861 869 lvals,
862 870 _line_format,
863 871 )
864 872 ),
865 873 )
866 874 # result += "\n".join(frame_info.raw_lines)
867 875 else:
868 876 result += "".join(
869 877 _format_traceback_lines(
870 878 frame_info.lines, Colors, self.has_colors, lvals
871 879 )
872 880 )
873 881 return result
874 882
875 def prepare_header(self, etype, long_version=False):
883 def prepare_header(self, etype: str, long_version: bool = False):
876 884 colors = self.Colors # just a shorthand + quicker name lookup
877 885 colorsnormal = colors.Normal # used a lot
878 886 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
879 887 width = min(75, get_terminal_size()[0])
880 888 if long_version:
881 889 # Header with the exception type, python version, and date
882 890 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
883 891 date = time.ctime(time.time())
884 892
885 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal,
886 exc, ' ' * (width - len(str(etype)) - len(pyver)),
887 pyver, date.rjust(width) )
888 head += "\nA problem occurred executing Python code. Here is the sequence of function" \
889 "\ncalls leading up to the error, with the most recent (innermost) call last."
893 head = "%s%s%s\n%s%s%s\n%s" % (
894 colors.topline,
895 "-" * width,
896 colorsnormal,
897 exc,
898 " " * (width - len(etype) - len(pyver)),
899 pyver,
900 date.rjust(width),
901 )
902 head += (
903 "\nA problem occurred executing Python code. Here is the sequence of function"
904 "\ncalls leading up to the error, with the most recent (innermost) call last."
905 )
890 906 else:
891 907 # Simplified header
892 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \
893 rjust(width - len(str(etype))) )
908 head = "%s%s" % (
909 exc,
910 "Traceback (most recent call last)".rjust(width - len(etype)),
911 )
894 912
895 913 return head
896 914
897 915 def format_exception(self, etype, evalue):
898 916 colors = self.Colors # just a shorthand + quicker name lookup
899 917 colorsnormal = colors.Normal # used a lot
900 918 # Get (safely) a string form of the exception info
901 919 try:
902 920 etype_str, evalue_str = map(str, (etype, evalue))
903 921 except:
904 922 # User exception is improperly defined.
905 923 etype, evalue = str, sys.exc_info()[:2]
906 924 etype_str, evalue_str = map(str, (etype, evalue))
907 925 # ... and format it
908 926 return ['%s%s%s: %s' % (colors.excName, etype_str,
909 927 colorsnormal, py3compat.cast_unicode(evalue_str))]
910 928
911 929 def format_exception_as_a_whole(
912 930 self,
913 931 etype: type,
914 evalue: BaseException,
932 evalue: Optional[BaseException],
915 933 etb: Optional[TracebackType],
916 934 number_of_lines_of_context,
917 935 tb_offset: Optional[int],
918 936 ):
919 937 """Formats the header, traceback and exception message for a single exception.
920 938
921 939 This may be called multiple times by Python 3 exception chaining
922 940 (PEP 3134).
923 941 """
924 942 # some locals
925 943 orig_etype = etype
926 944 try:
927 945 etype = etype.__name__
928 946 except AttributeError:
929 947 pass
930 948
931 949 tb_offset = self.tb_offset if tb_offset is None else tb_offset
932 950 assert isinstance(tb_offset, int)
933 951 head = self.prepare_header(etype, self.long_header)
934 952 records = (
935 953 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
936 954 )
937 955
938 956 frames = []
939 957 skipped = 0
940 958 lastrecord = len(records) - 1
941 959 for i, record in enumerate(records):
942 960 if (
943 961 not isinstance(record._sd, stack_data.RepeatedFrames)
944 962 and self.skip_hidden
945 963 ):
946 964 if (
947 965 record.frame.f_locals.get("__tracebackhide__", 0)
948 966 and i != lastrecord
949 967 ):
950 968 skipped += 1
951 969 continue
952 970 if skipped:
953 971 Colors = self.Colors # just a shorthand + quicker name lookup
954 972 ColorsNormal = Colors.Normal # used a lot
955 973 frames.append(
956 974 " %s[... skipping hidden %s frame]%s\n"
957 975 % (Colors.excName, skipped, ColorsNormal)
958 976 )
959 977 skipped = 0
960 978 frames.append(self.format_record(record))
961 979 if skipped:
962 980 Colors = self.Colors # just a shorthand + quicker name lookup
963 981 ColorsNormal = Colors.Normal # used a lot
964 982 frames.append(
965 983 " %s[... skipping hidden %s frame]%s\n"
966 984 % (Colors.excName, skipped, ColorsNormal)
967 985 )
968 986
969 987 formatted_exception = self.format_exception(etype, evalue)
970 988 if records:
971 989 frame_info = records[-1]
972 990 ipinst = get_ipython()
973 991 if ipinst is not None:
974 992 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
975 993
976 994 return [[head] + frames + [''.join(formatted_exception[0])]]
977 995
978 996 def get_records(
979 997 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
980 998 ):
981 999 assert etb is not None
982 1000 context = number_of_lines_of_context - 1
983 1001 after = context // 2
984 1002 before = context - after
985 1003 if self.has_colors:
986 1004 style = get_style_by_name("default")
987 1005 style = stack_data.style_with_executing_node(style, self._tb_highlight)
988 1006 formatter = Terminal256Formatter(style=style)
989 1007 else:
990 1008 formatter = None
991 1009 options = stack_data.Options(
992 1010 before=before,
993 1011 after=after,
994 1012 pygments_formatter=formatter,
995 1013 )
996 1014
997 1015 # Let's estimate the amount of code we will have to parse/highlight.
998 cf = etb
1016 cf: Optional[TracebackType] = etb
999 1017 max_len = 0
1000 1018 tbs = []
1001 1019 while cf is not None:
1002 1020 source_file = inspect.getsourcefile(etb.tb_frame)
1003 1021 lines, first = inspect.getsourcelines(etb.tb_frame)
1004 1022 max_len = max(max_len, first + len(lines))
1005 1023 tbs.append(cf)
1006 1024 cf = cf.tb_next
1007 1025
1008 1026 if max_len > FAST_THRESHOLD:
1009 1027 FIs = []
1010 1028 for tb in tbs:
1011 1029 frame = tb.tb_frame
1012 1030 lineno = (frame.f_lineno,)
1013 1031 code = frame.f_code
1014 1032 filename = code.co_filename
1015 1033 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code))
1016 1034 return FIs
1017 1035 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1018 1036 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1019 1037 return res
1020 1038
1021 1039 def structured_traceback(
1022 1040 self,
1023 1041 etype: type,
1024 1042 evalue: Optional[BaseException],
1025 1043 etb: Optional[TracebackType],
1026 1044 tb_offset: Optional[int] = None,
1027 1045 number_of_lines_of_context: int = 5,
1028 1046 ):
1029 1047 """Return a nice text document describing the traceback."""
1030 1048 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1031 1049 tb_offset)
1032 1050
1033 1051 colors = self.Colors # just a shorthand + quicker name lookup
1034 1052 colorsnormal = colors.Normal # used a lot
1035 1053 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1036 1054 structured_traceback_parts = [head]
1037 1055 chained_exceptions_tb_offset = 0
1038 1056 lines_of_context = 3
1039 1057 formatted_exceptions = formatted_exception
1040 1058 exception = self.get_parts_of_chained_exception(evalue)
1041 1059 if exception:
1042 1060 assert evalue is not None
1043 1061 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1044 1062 etype, evalue, etb = exception
1045 1063 else:
1046 1064 evalue = None
1047 1065 chained_exc_ids = set()
1048 1066 while evalue:
1049 1067 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1050 1068 chained_exceptions_tb_offset)
1051 1069 exception = self.get_parts_of_chained_exception(evalue)
1052 1070
1053 1071 if exception and not id(exception[1]) in chained_exc_ids:
1054 1072 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1055 1073 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1056 1074 etype, evalue, etb = exception
1057 1075 else:
1058 1076 evalue = None
1059 1077
1060 1078 # we want to see exceptions in a reversed order:
1061 1079 # the first exception should be on top
1062 1080 for formatted_exception in reversed(formatted_exceptions):
1063 1081 structured_traceback_parts += formatted_exception
1064 1082
1065 1083 return structured_traceback_parts
1066 1084
1067 1085 def debugger(self, force: bool = False):
1068 1086 """Call up the pdb debugger if desired, always clean up the tb
1069 1087 reference.
1070 1088
1071 1089 Keywords:
1072 1090
1073 1091 - force(False): by default, this routine checks the instance call_pdb
1074 1092 flag and does not actually invoke the debugger if the flag is false.
1075 1093 The 'force' option forces the debugger to activate even if the flag
1076 1094 is false.
1077 1095
1078 1096 If the call_pdb flag is set, the pdb interactive debugger is
1079 1097 invoked. In all cases, the self.tb reference to the current traceback
1080 1098 is deleted to prevent lingering references which hamper memory
1081 1099 management.
1082 1100
1083 1101 Note that each call to pdb() does an 'import readline', so if your app
1084 1102 requires a special setup for the readline completers, you'll have to
1085 1103 fix that by hand after invoking the exception handler."""
1086 1104
1087 1105 if force or self.call_pdb:
1088 1106 if self.pdb is None:
1089 1107 self.pdb = self.debugger_cls()
1090 1108 # the system displayhook may have changed, restore the original
1091 1109 # for pdb
1092 1110 display_trap = DisplayTrap(hook=sys.__displayhook__)
1093 1111 with display_trap:
1094 1112 self.pdb.reset()
1095 1113 # Find the right frame so we don't pop up inside ipython itself
1096 1114 if hasattr(self, 'tb') and self.tb is not None:
1097 1115 etb = self.tb
1098 1116 else:
1099 1117 etb = self.tb = sys.last_traceback
1100 1118 while self.tb is not None and self.tb.tb_next is not None:
1101 1119 assert self.tb.tb_next is not None
1102 1120 self.tb = self.tb.tb_next
1103 1121 if etb and etb.tb_next:
1104 1122 etb = etb.tb_next
1105 1123 self.pdb.botframe = etb.tb_frame
1106 1124 self.pdb.interaction(None, etb)
1107 1125
1108 1126 if hasattr(self, 'tb'):
1109 1127 del self.tb
1110 1128
1111 1129 def handler(self, info=None):
1112 1130 (etype, evalue, etb) = info or sys.exc_info()
1113 1131 self.tb = etb
1114 1132 ostream = self.ostream
1115 1133 ostream.flush()
1116 1134 ostream.write(self.text(etype, evalue, etb))
1117 1135 ostream.write('\n')
1118 1136 ostream.flush()
1119 1137
1120 1138 # Changed so an instance can just be called as VerboseTB_inst() and print
1121 1139 # out the right info on its own.
1122 1140 def __call__(self, etype=None, evalue=None, etb=None):
1123 1141 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1124 1142 if etb is None:
1125 1143 self.handler()
1126 1144 else:
1127 1145 self.handler((etype, evalue, etb))
1128 1146 try:
1129 1147 self.debugger()
1130 1148 except KeyboardInterrupt:
1131 1149 print("\nKeyboardInterrupt")
1132 1150
1133 1151
1134 1152 #----------------------------------------------------------------------------
1135 1153 class FormattedTB(VerboseTB, ListTB):
1136 1154 """Subclass ListTB but allow calling with a traceback.
1137 1155
1138 1156 It can thus be used as a sys.excepthook for Python > 2.1.
1139 1157
1140 1158 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1141 1159
1142 1160 Allows a tb_offset to be specified. This is useful for situations where
1143 1161 one needs to remove a number of topmost frames from the traceback (such as
1144 1162 occurs with python programs that themselves execute other python code,
1145 1163 like Python shells). """
1146 1164
1147 1165 mode: str
1148 1166
1149 1167 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1150 1168 ostream=None,
1151 1169 tb_offset=0, long_header=False, include_vars=False,
1152 1170 check_cache=None, debugger_cls=None,
1153 1171 parent=None, config=None):
1154 1172
1155 1173 # NEVER change the order of this list. Put new modes at the end:
1156 1174 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1157 1175 self.verbose_modes = self.valid_modes[1:3]
1158 1176
1159 1177 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1160 1178 ostream=ostream, tb_offset=tb_offset,
1161 1179 long_header=long_header, include_vars=include_vars,
1162 1180 check_cache=check_cache, debugger_cls=debugger_cls,
1163 1181 parent=parent, config=config)
1164 1182
1165 1183 # Different types of tracebacks are joined with different separators to
1166 1184 # form a single string. They are taken from this dict
1167 1185 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1168 1186 Minimal='')
1169 1187 # set_mode also sets the tb_join_char attribute
1170 1188 self.set_mode(mode)
1171 1189
1172 1190 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1173 1191 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1174 1192 mode = self.mode
1175 1193 if mode in self.verbose_modes:
1176 1194 # Verbose modes need a full traceback
1177 1195 return VerboseTB.structured_traceback(
1178 1196 self, etype, value, tb, tb_offset, number_of_lines_of_context
1179 1197 )
1180 1198 elif mode == 'Minimal':
1181 1199 return ListTB.get_exception_only(self, etype, value)
1182 1200 else:
1183 1201 # We must check the source cache because otherwise we can print
1184 1202 # out-of-date source code.
1185 1203 self.check_cache()
1186 1204 # Now we can extract and format the exception
1187 1205 return ListTB.structured_traceback(
1188 1206 self, etype, value, tb, tb_offset, number_of_lines_of_context
1189 1207 )
1190 1208
1191 1209 def stb2text(self, stb):
1192 1210 """Convert a structured traceback (a list) to a string."""
1193 1211 return self.tb_join_char.join(stb)
1194 1212
1195 1213 def set_mode(self, mode: Optional[str] = None):
1196 1214 """Switch to the desired mode.
1197 1215
1198 1216 If mode is not specified, cycles through the available modes."""
1199 1217
1200 1218 if not mode:
1201 1219 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1202 1220 len(self.valid_modes)
1203 1221 self.mode = self.valid_modes[new_idx]
1204 1222 elif mode not in self.valid_modes:
1205 1223 raise ValueError(
1206 1224 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1207 1225 "Valid modes: " + str(self.valid_modes)
1208 1226 )
1209 1227 else:
1210 1228 assert isinstance(mode, str)
1211 1229 self.mode = mode
1212 1230 # include variable details only in 'Verbose' mode
1213 1231 self.include_vars = (self.mode == self.valid_modes[2])
1214 1232 # Set the join character for generating text tracebacks
1215 1233 self.tb_join_char = self._join_chars[self.mode]
1216 1234
1217 1235 # some convenient shortcuts
1218 1236 def plain(self):
1219 1237 self.set_mode(self.valid_modes[0])
1220 1238
1221 1239 def context(self):
1222 1240 self.set_mode(self.valid_modes[1])
1223 1241
1224 1242 def verbose(self):
1225 1243 self.set_mode(self.valid_modes[2])
1226 1244
1227 1245 def minimal(self):
1228 1246 self.set_mode(self.valid_modes[3])
1229 1247
1230 1248
1231 1249 #----------------------------------------------------------------------------
1232 1250 class AutoFormattedTB(FormattedTB):
1233 1251 """A traceback printer which can be called on the fly.
1234 1252
1235 1253 It will find out about exceptions by itself.
1236 1254
1237 1255 A brief example::
1238 1256
1239 1257 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1240 1258 try:
1241 1259 ...
1242 1260 except:
1243 1261 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1244 1262 """
1245 1263
1246 1264 def __call__(self, etype=None, evalue=None, etb=None,
1247 1265 out=None, tb_offset=None):
1248 1266 """Print out a formatted exception traceback.
1249 1267
1250 1268 Optional arguments:
1251 1269 - out: an open file-like object to direct output to.
1252 1270
1253 1271 - tb_offset: the number of frames to skip over in the stack, on a
1254 1272 per-call basis (this overrides temporarily the instance's tb_offset
1255 1273 given at initialization time."""
1256 1274
1257 1275 if out is None:
1258 1276 out = self.ostream
1259 1277 out.flush()
1260 1278 out.write(self.text(etype, evalue, etb, tb_offset))
1261 1279 out.write('\n')
1262 1280 out.flush()
1263 1281 # FIXME: we should remove the auto pdb behavior from here and leave
1264 1282 # that to the clients.
1265 1283 try:
1266 1284 self.debugger()
1267 1285 except KeyboardInterrupt:
1268 1286 print("\nKeyboardInterrupt")
1269 1287
1270 1288 def structured_traceback(
1271 1289 self,
1272 1290 etype=None,
1273 1291 value=None,
1274 1292 tb=None,
1275 1293 tb_offset=None,
1276 1294 number_of_lines_of_context=5,
1277 1295 ):
1278 1296 etype: type
1279 1297 value: BaseException
1280 1298 # tb: TracebackType or tupleof tb types ?
1281 1299 if etype is None:
1282 1300 etype, value, tb = sys.exc_info()
1283 1301 if isinstance(tb, tuple):
1284 1302 # tb is a tuple if this is a chained exception.
1285 1303 self.tb = tb[0]
1286 1304 else:
1287 1305 self.tb = tb
1288 1306 return FormattedTB.structured_traceback(
1289 1307 self, etype, value, tb, tb_offset, number_of_lines_of_context)
1290 1308
1291 1309
1292 1310 #---------------------------------------------------------------------------
1293 1311
1294 1312 # A simple class to preserve Nathan's original functionality.
1295 1313 class ColorTB(FormattedTB):
1296 1314 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1297 1315
1298 1316 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1299 1317 FormattedTB.__init__(self, color_scheme=color_scheme,
1300 1318 call_pdb=call_pdb, **kwargs)
1301 1319
1302 1320
1303 1321 class SyntaxTB(ListTB):
1304 1322 """Extension which holds some state: the last exception value"""
1305 1323
1306 1324 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1307 1325 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1308 1326 self.last_syntax_error = None
1309 1327
1310 1328 def __call__(self, etype, value, elist):
1311 1329 self.last_syntax_error = value
1312 1330
1313 1331 ListTB.__call__(self, etype, value, elist)
1314 1332
1315 1333 def structured_traceback(self, etype, value, elist, tb_offset=None,
1316 1334 context=5):
1317 1335 # If the source file has been edited, the line in the syntax error can
1318 1336 # be wrong (retrieved from an outdated cache). This replaces it with
1319 1337 # the current value.
1320 1338 if isinstance(value, SyntaxError) \
1321 1339 and isinstance(value.filename, str) \
1322 1340 and isinstance(value.lineno, int):
1323 1341 linecache.checkcache(value.filename)
1324 1342 newtext = linecache.getline(value.filename, value.lineno)
1325 1343 if newtext:
1326 1344 value.text = newtext
1327 1345 self.last_syntax_error = value
1328 1346 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1329 1347 tb_offset=tb_offset, context=context)
1330 1348
1331 1349 def clear_err_state(self):
1332 1350 """Return the current error state and clear it"""
1333 1351 e = self.last_syntax_error
1334 1352 self.last_syntax_error = None
1335 1353 return e
1336 1354
1337 1355 def stb2text(self, stb):
1338 1356 """Convert a structured traceback (a list) to a string."""
1339 1357 return ''.join(stb)
1340 1358
1341 1359
1342 1360 # some internal-use functions
1343 1361 def text_repr(value):
1344 1362 """Hopefully pretty robust repr equivalent."""
1345 1363 # this is pretty horrible but should always return *something*
1346 1364 try:
1347 1365 return pydoc.text.repr(value)
1348 1366 except KeyboardInterrupt:
1349 1367 raise
1350 1368 except:
1351 1369 try:
1352 1370 return repr(value)
1353 1371 except KeyboardInterrupt:
1354 1372 raise
1355 1373 except:
1356 1374 try:
1357 1375 # all still in an except block so we catch
1358 1376 # getattr raising
1359 1377 name = getattr(value, '__name__', None)
1360 1378 if name:
1361 1379 # ick, recursion
1362 1380 return text_repr(name)
1363 1381 klass = getattr(value, '__class__', None)
1364 1382 if klass:
1365 1383 return '%s instance' % text_repr(klass)
1366 1384 except KeyboardInterrupt:
1367 1385 raise
1368 1386 except:
1369 1387 return 'UNRECOVERABLE REPR FAILURE'
1370 1388
1371 1389
1372 1390 def eqrepr(value, repr=text_repr):
1373 1391 return '=%s' % repr(value)
1374 1392
1375 1393
1376 1394 def nullrepr(value, repr=text_repr):
1377 1395 return ''
@@ -1,675 +1,677 b''
1 1 """Various display related classes.
2 2
3 3 Authors : MinRK, gregcaporaso, dannystaple
4 4 """
5 5 from html import escape as html_escape
6 6 from os.path import exists, isfile, splitext, abspath, join, isdir
7 7 from os import walk, sep, fsdecode
8 8
9 9 from IPython.core.display import DisplayObject, TextDisplayObject
10 10
11 from typing import Tuple, Iterable
11 from typing import Tuple, Iterable, Optional
12 12
13 13 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
14 14 'FileLink', 'FileLinks', 'Code']
15 15
16 16
17 17 class Audio(DisplayObject):
18 18 """Create an audio object.
19 19
20 20 When this object is returned by an input cell or passed to the
21 21 display function, it will result in Audio controls being displayed
22 22 in the frontend (only works in the notebook).
23 23
24 24 Parameters
25 25 ----------
26 26 data : numpy array, list, unicode, str or bytes
27 27 Can be one of
28 28
29 29 * Numpy 1d array containing the desired waveform (mono)
30 30 * Numpy 2d array containing waveforms for each channel.
31 31 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
32 32 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
33 33 * List of float or integer representing the waveform (mono)
34 34 * String containing the filename
35 35 * Bytestring containing raw PCM data or
36 36 * URL pointing to a file on the web.
37 37
38 38 If the array option is used, the waveform will be normalized.
39 39
40 40 If a filename or url is used, the format support will be browser
41 41 dependent.
42 42 url : unicode
43 43 A URL to download the data from.
44 44 filename : unicode
45 45 Path to a local file to load the data from.
46 46 embed : boolean
47 47 Should the audio data be embedded using a data URI (True) or should
48 48 the original source be referenced. Set this to True if you want the
49 49 audio to playable later with no internet connection in the notebook.
50 50
51 51 Default is `True`, unless the keyword argument `url` is set, then
52 52 default value is `False`.
53 53 rate : integer
54 54 The sampling rate of the raw data.
55 55 Only required when data parameter is being used as an array
56 56 autoplay : bool
57 57 Set to True if the audio should immediately start playing.
58 58 Default is `False`.
59 59 normalize : bool
60 60 Whether audio should be normalized (rescaled) to the maximum possible
61 61 range. Default is `True`. When set to `False`, `data` must be between
62 62 -1 and 1 (inclusive), otherwise an error is raised.
63 63 Applies only when `data` is a list or array of samples; other types of
64 64 audio are never normalized.
65 65
66 66 Examples
67 67 --------
68 68
69 69 >>> import pytest
70 70 >>> np = pytest.importorskip("numpy")
71 71
72 72 Generate a sound
73 73
74 74 >>> import numpy as np
75 75 >>> framerate = 44100
76 76 >>> t = np.linspace(0,5,framerate*5)
77 77 >>> data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t)
78 78 >>> Audio(data, rate=framerate)
79 79 <IPython.lib.display.Audio object>
80 80
81 81 Can also do stereo or more channels
82 82
83 83 >>> dataleft = np.sin(2*np.pi*220*t)
84 84 >>> dataright = np.sin(2*np.pi*224*t)
85 85 >>> Audio([dataleft, dataright], rate=framerate)
86 86 <IPython.lib.display.Audio object>
87 87
88 88 From URL:
89 89
90 90 >>> Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # doctest: +SKIP
91 91 >>> Audio(url="http://www.w3schools.com/html/horse.ogg") # doctest: +SKIP
92 92
93 93 From a File:
94 94
95 95 >>> Audio('IPython/lib/tests/test.wav') # doctest: +SKIP
96 96 >>> Audio(filename='IPython/lib/tests/test.wav') # doctest: +SKIP
97 97
98 98 From Bytes:
99 99
100 100 >>> Audio(b'RAW_WAV_DATA..') # doctest: +SKIP
101 101 >>> Audio(data=b'RAW_WAV_DATA..') # doctest: +SKIP
102 102
103 103 See Also
104 104 --------
105 105 ipywidgets.Audio
106 106
107 107 Audio widget with more more flexibility and options.
108 108
109 109 """
110 110 _read_flags = 'rb'
111 111
112 112 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *,
113 113 element_id=None):
114 114 if filename is None and url is None and data is None:
115 115 raise ValueError("No audio data found. Expecting filename, url, or data.")
116 116 if embed is False and url is None:
117 117 raise ValueError("No url found. Expecting url when embed=False")
118 118
119 119 if url is not None and embed is not True:
120 120 self.embed = False
121 121 else:
122 122 self.embed = True
123 123 self.autoplay = autoplay
124 124 self.element_id = element_id
125 125 super(Audio, self).__init__(data=data, url=url, filename=filename)
126 126
127 127 if self.data is not None and not isinstance(self.data, bytes):
128 128 if rate is None:
129 129 raise ValueError("rate must be specified when data is a numpy array or list of audio samples.")
130 130 self.data = Audio._make_wav(data, rate, normalize)
131 131
132 132 def reload(self):
133 133 """Reload the raw data from file or URL."""
134 134 import mimetypes
135 135 if self.embed:
136 136 super(Audio, self).reload()
137 137
138 138 if self.filename is not None:
139 139 self.mimetype = mimetypes.guess_type(self.filename)[0]
140 140 elif self.url is not None:
141 141 self.mimetype = mimetypes.guess_type(self.url)[0]
142 142 else:
143 143 self.mimetype = "audio/wav"
144 144
145 145 @staticmethod
146 146 def _make_wav(data, rate, normalize):
147 147 """ Transform a numpy array to a PCM bytestring """
148 148 from io import BytesIO
149 149 import wave
150 150
151 151 try:
152 152 scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize)
153 153 except ImportError:
154 154 scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize)
155 155
156 156 fp = BytesIO()
157 157 waveobj = wave.open(fp,mode='wb')
158 158 waveobj.setnchannels(nchan)
159 159 waveobj.setframerate(rate)
160 160 waveobj.setsampwidth(2)
161 161 waveobj.setcomptype('NONE','NONE')
162 162 waveobj.writeframes(scaled)
163 163 val = fp.getvalue()
164 164 waveobj.close()
165 165
166 166 return val
167 167
168 168 @staticmethod
169 169 def _validate_and_normalize_with_numpy(data, normalize) -> Tuple[bytes, int]:
170 170 import numpy as np
171 171
172 172 data = np.array(data, dtype=float)
173 173 if len(data.shape) == 1:
174 174 nchan = 1
175 175 elif len(data.shape) == 2:
176 176 # In wave files,channels are interleaved. E.g.,
177 177 # "L1R1L2R2..." for stereo. See
178 178 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
179 179 # for channel ordering
180 180 nchan = data.shape[0]
181 181 data = data.T.ravel()
182 182 else:
183 183 raise ValueError('Array audio input must be a 1D or 2D array')
184 184
185 185 max_abs_value = np.max(np.abs(data))
186 186 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
187 187 scaled = data / normalization_factor * 32767
188 188 return scaled.astype("<h").tobytes(), nchan
189 189
190 190 @staticmethod
191 191 def _validate_and_normalize_without_numpy(data, normalize):
192 192 import array
193 193 import sys
194 194
195 195 data = array.array('f', data)
196 196
197 197 try:
198 198 max_abs_value = float(max([abs(x) for x in data]))
199 199 except TypeError as e:
200 200 raise TypeError('Only lists of mono audio are '
201 201 'supported if numpy is not installed') from e
202 202
203 203 normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize)
204 204 scaled = array.array('h', [int(x / normalization_factor * 32767) for x in data])
205 205 if sys.byteorder == 'big':
206 206 scaled.byteswap()
207 207 nchan = 1
208 208 return scaled.tobytes(), nchan
209 209
210 210 @staticmethod
211 211 def _get_normalization_factor(max_abs_value, normalize):
212 212 if not normalize and max_abs_value > 1:
213 213 raise ValueError('Audio data must be between -1 and 1 when normalize=False.')
214 214 return max_abs_value if normalize else 1
215 215
216 216 def _data_and_metadata(self):
217 217 """shortcut for returning metadata with url information, if defined"""
218 218 md = {}
219 219 if self.url:
220 220 md['url'] = self.url
221 221 if md:
222 222 return self.data, md
223 223 else:
224 224 return self.data
225 225
226 226 def _repr_html_(self):
227 227 src = """
228 228 <audio {element_id} controls="controls" {autoplay}>
229 229 <source src="{src}" type="{type}" />
230 230 Your browser does not support the audio element.
231 231 </audio>
232 232 """
233 233 return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(),
234 234 element_id=self.element_id_attr())
235 235
236 236 def src_attr(self):
237 237 import base64
238 238 if self.embed and (self.data is not None):
239 239 data = base64=base64.b64encode(self.data).decode('ascii')
240 240 return """data:{type};base64,{base64}""".format(type=self.mimetype,
241 241 base64=data)
242 242 elif self.url is not None:
243 243 return self.url
244 244 else:
245 245 return ""
246 246
247 247 def autoplay_attr(self):
248 248 if(self.autoplay):
249 249 return 'autoplay="autoplay"'
250 250 else:
251 251 return ''
252 252
253 253 def element_id_attr(self):
254 254 if (self.element_id):
255 255 return 'id="{element_id}"'.format(element_id=self.element_id)
256 256 else:
257 257 return ''
258 258
259 259 class IFrame(object):
260 260 """
261 261 Generic class to embed an iframe in an IPython notebook
262 262 """
263 263
264 264 iframe = """
265 265 <iframe
266 266 width="{width}"
267 267 height="{height}"
268 268 src="{src}{params}"
269 269 frameborder="0"
270 270 allowfullscreen
271 271 {extras}
272 272 ></iframe>
273 273 """
274 274
275 def __init__(self, src, width, height, extras: Iterable[str] = None, **kwargs):
275 def __init__(
276 self, src, width, height, extras: Optional[Iterable[str]] = None, **kwargs
277 ):
276 278 if extras is None:
277 279 extras = []
278 280
279 281 self.src = src
280 282 self.width = width
281 283 self.height = height
282 284 self.extras = extras
283 285 self.params = kwargs
284 286
285 287 def _repr_html_(self):
286 288 """return the embed iframe"""
287 289 if self.params:
288 290 from urllib.parse import urlencode
289 291 params = "?" + urlencode(self.params)
290 292 else:
291 293 params = ""
292 294 return self.iframe.format(
293 295 src=self.src,
294 296 width=self.width,
295 297 height=self.height,
296 298 params=params,
297 299 extras=" ".join(self.extras),
298 300 )
299 301
300 302
301 303 class YouTubeVideo(IFrame):
302 304 """Class for embedding a YouTube Video in an IPython session, based on its video id.
303 305
304 306 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
305 307 do::
306 308
307 309 vid = YouTubeVideo("foo")
308 310 display(vid)
309 311
310 312 To start from 30 seconds::
311 313
312 314 vid = YouTubeVideo("abc", start=30)
313 315 display(vid)
314 316
315 317 To calculate seconds from time as hours, minutes, seconds use
316 318 :class:`datetime.timedelta`::
317 319
318 320 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
319 321
320 322 Other parameters can be provided as documented at
321 323 https://developers.google.com/youtube/player_parameters#Parameters
322 324
323 325 When converting the notebook using nbconvert, a jpeg representation of the video
324 326 will be inserted in the document.
325 327 """
326 328
327 329 def __init__(self, id, width=400, height=300, allow_autoplay=False, **kwargs):
328 330 self.id=id
329 331 src = "https://www.youtube.com/embed/{0}".format(id)
330 332 if allow_autoplay:
331 333 extras = list(kwargs.get("extras", [])) + ['allow="autoplay"']
332 334 kwargs.update(autoplay=1, extras=extras)
333 335 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
334 336
335 337 def _repr_jpeg_(self):
336 338 # Deferred import
337 339 from urllib.request import urlopen
338 340
339 341 try:
340 342 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
341 343 except IOError:
342 344 return None
343 345
344 346 class VimeoVideo(IFrame):
345 347 """
346 348 Class for embedding a Vimeo video in an IPython session, based on its video id.
347 349 """
348 350
349 351 def __init__(self, id, width=400, height=300, **kwargs):
350 352 src="https://player.vimeo.com/video/{0}".format(id)
351 353 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
352 354
353 355 class ScribdDocument(IFrame):
354 356 """
355 357 Class for embedding a Scribd document in an IPython session
356 358
357 359 Use the start_page params to specify a starting point in the document
358 360 Use the view_mode params to specify display type one off scroll | slideshow | book
359 361
360 362 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
361 363
362 364 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
363 365 """
364 366
365 367 def __init__(self, id, width=400, height=300, **kwargs):
366 368 src="https://www.scribd.com/embeds/{0}/content".format(id)
367 369 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
368 370
369 371 class FileLink(object):
370 372 """Class for embedding a local file link in an IPython session, based on path
371 373
372 374 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
373 375
374 376 you would do::
375 377
376 378 local_file = FileLink("my/data.txt")
377 379 display(local_file)
378 380
379 381 or in the HTML notebook, just::
380 382
381 383 FileLink("my/data.txt")
382 384 """
383 385
384 386 html_link_str = "<a href='%s' target='_blank'>%s</a>"
385 387
386 388 def __init__(self,
387 389 path,
388 390 url_prefix='',
389 391 result_html_prefix='',
390 392 result_html_suffix='<br>'):
391 393 """
392 394 Parameters
393 395 ----------
394 396 path : str
395 397 path to the file or directory that should be formatted
396 398 url_prefix : str
397 399 prefix to be prepended to all files to form a working link [default:
398 400 '']
399 401 result_html_prefix : str
400 402 text to append to beginning to link [default: '']
401 403 result_html_suffix : str
402 404 text to append at the end of link [default: '<br>']
403 405 """
404 406 if isdir(path):
405 407 raise ValueError("Cannot display a directory using FileLink. "
406 408 "Use FileLinks to display '%s'." % path)
407 409 self.path = fsdecode(path)
408 410 self.url_prefix = url_prefix
409 411 self.result_html_prefix = result_html_prefix
410 412 self.result_html_suffix = result_html_suffix
411 413
412 414 def _format_path(self):
413 415 fp = ''.join([self.url_prefix, html_escape(self.path)])
414 416 return ''.join([self.result_html_prefix,
415 417 self.html_link_str % \
416 418 (fp, html_escape(self.path, quote=False)),
417 419 self.result_html_suffix])
418 420
419 421 def _repr_html_(self):
420 422 """return html link to file
421 423 """
422 424 if not exists(self.path):
423 425 return ("Path (<tt>%s</tt>) doesn't exist. "
424 426 "It may still be in the process of "
425 427 "being generated, or you may have the "
426 428 "incorrect path." % self.path)
427 429
428 430 return self._format_path()
429 431
430 432 def __repr__(self):
431 433 """return absolute path to file
432 434 """
433 435 return abspath(self.path)
434 436
435 437 class FileLinks(FileLink):
436 438 """Class for embedding local file links in an IPython session, based on path
437 439
438 440 e.g. to embed links to files that were generated in the IPython notebook
439 441 under ``my/data``, you would do::
440 442
441 443 local_files = FileLinks("my/data")
442 444 display(local_files)
443 445
444 446 or in the HTML notebook, just::
445 447
446 448 FileLinks("my/data")
447 449 """
448 450 def __init__(self,
449 451 path,
450 452 url_prefix='',
451 453 included_suffixes=None,
452 454 result_html_prefix='',
453 455 result_html_suffix='<br>',
454 456 notebook_display_formatter=None,
455 457 terminal_display_formatter=None,
456 458 recursive=True):
457 459 """
458 460 See :class:`FileLink` for the ``path``, ``url_prefix``,
459 461 ``result_html_prefix`` and ``result_html_suffix`` parameters.
460 462
461 463 included_suffixes : list
462 464 Filename suffixes to include when formatting output [default: include
463 465 all files]
464 466
465 467 notebook_display_formatter : function
466 468 Used to format links for display in the notebook. See discussion of
467 469 formatter functions below.
468 470
469 471 terminal_display_formatter : function
470 472 Used to format links for display in the terminal. See discussion of
471 473 formatter functions below.
472 474
473 475 Formatter functions must be of the form::
474 476
475 477 f(dirname, fnames, included_suffixes)
476 478
477 479 dirname : str
478 480 The name of a directory
479 481 fnames : list
480 482 The files in that directory
481 483 included_suffixes : list
482 484 The file suffixes that should be included in the output (passing None
483 485 meansto include all suffixes in the output in the built-in formatters)
484 486 recursive : boolean
485 487 Whether to recurse into subdirectories. Default is True.
486 488
487 489 The function should return a list of lines that will be printed in the
488 490 notebook (if passing notebook_display_formatter) or the terminal (if
489 491 passing terminal_display_formatter). This function is iterated over for
490 492 each directory in self.path. Default formatters are in place, can be
491 493 passed here to support alternative formatting.
492 494
493 495 """
494 496 if isfile(path):
495 497 raise ValueError("Cannot display a file using FileLinks. "
496 498 "Use FileLink to display '%s'." % path)
497 499 self.included_suffixes = included_suffixes
498 500 # remove trailing slashes for more consistent output formatting
499 501 path = path.rstrip('/')
500 502
501 503 self.path = path
502 504 self.url_prefix = url_prefix
503 505 self.result_html_prefix = result_html_prefix
504 506 self.result_html_suffix = result_html_suffix
505 507
506 508 self.notebook_display_formatter = \
507 509 notebook_display_formatter or self._get_notebook_display_formatter()
508 510 self.terminal_display_formatter = \
509 511 terminal_display_formatter or self._get_terminal_display_formatter()
510 512
511 513 self.recursive = recursive
512 514
513 515 def _get_display_formatter(
514 516 self, dirname_output_format, fname_output_format, fp_format, fp_cleaner=None
515 517 ):
516 518 """generate built-in formatter function
517 519
518 520 this is used to define both the notebook and terminal built-in
519 521 formatters as they only differ by some wrapper text for each entry
520 522
521 523 dirname_output_format: string to use for formatting directory
522 524 names, dirname will be substituted for a single "%s" which
523 525 must appear in this string
524 526 fname_output_format: string to use for formatting file names,
525 527 if a single "%s" appears in the string, fname will be substituted
526 528 if two "%s" appear in the string, the path to fname will be
527 529 substituted for the first and fname will be substituted for the
528 530 second
529 531 fp_format: string to use for formatting filepaths, must contain
530 532 exactly two "%s" and the dirname will be substituted for the first
531 533 and fname will be substituted for the second
532 534 """
533 535 def f(dirname, fnames, included_suffixes=None):
534 536 result = []
535 537 # begin by figuring out which filenames, if any,
536 538 # are going to be displayed
537 539 display_fnames = []
538 540 for fname in fnames:
539 541 if (isfile(join(dirname,fname)) and
540 542 (included_suffixes is None or
541 543 splitext(fname)[1] in included_suffixes)):
542 544 display_fnames.append(fname)
543 545
544 546 if len(display_fnames) == 0:
545 547 # if there are no filenames to display, don't print anything
546 548 # (not even the directory name)
547 549 pass
548 550 else:
549 551 # otherwise print the formatted directory name followed by
550 552 # the formatted filenames
551 553 dirname_output_line = dirname_output_format % dirname
552 554 result.append(dirname_output_line)
553 555 for fname in display_fnames:
554 556 fp = fp_format % (dirname,fname)
555 557 if fp_cleaner is not None:
556 558 fp = fp_cleaner(fp)
557 559 try:
558 560 # output can include both a filepath and a filename...
559 561 fname_output_line = fname_output_format % (fp, fname)
560 562 except TypeError:
561 563 # ... or just a single filepath
562 564 fname_output_line = fname_output_format % fname
563 565 result.append(fname_output_line)
564 566 return result
565 567 return f
566 568
567 569 def _get_notebook_display_formatter(self,
568 570 spacer="&nbsp;&nbsp;"):
569 571 """ generate function to use for notebook formatting
570 572 """
571 573 dirname_output_format = \
572 574 self.result_html_prefix + "%s/" + self.result_html_suffix
573 575 fname_output_format = \
574 576 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
575 577 fp_format = self.url_prefix + '%s/%s'
576 578 if sep == "\\":
577 579 # Working on a platform where the path separator is "\", so
578 580 # must convert these to "/" for generating a URI
579 581 def fp_cleaner(fp):
580 582 # Replace all occurrences of backslash ("\") with a forward
581 583 # slash ("/") - this is necessary on windows when a path is
582 584 # provided as input, but we must link to a URI
583 585 return fp.replace('\\','/')
584 586 else:
585 587 fp_cleaner = None
586 588
587 589 return self._get_display_formatter(dirname_output_format,
588 590 fname_output_format,
589 591 fp_format,
590 592 fp_cleaner)
591 593
592 594 def _get_terminal_display_formatter(self,
593 595 spacer=" "):
594 596 """ generate function to use for terminal formatting
595 597 """
596 598 dirname_output_format = "%s/"
597 599 fname_output_format = spacer + "%s"
598 600 fp_format = '%s/%s'
599 601
600 602 return self._get_display_formatter(dirname_output_format,
601 603 fname_output_format,
602 604 fp_format)
603 605
604 606 def _format_path(self):
605 607 result_lines = []
606 608 if self.recursive:
607 609 walked_dir = list(walk(self.path))
608 610 else:
609 611 walked_dir = [next(walk(self.path))]
610 612 walked_dir.sort()
611 613 for dirname, subdirs, fnames in walked_dir:
612 614 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
613 615 return '\n'.join(result_lines)
614 616
615 617 def __repr__(self):
616 618 """return newline-separated absolute paths
617 619 """
618 620 result_lines = []
619 621 if self.recursive:
620 622 walked_dir = list(walk(self.path))
621 623 else:
622 624 walked_dir = [next(walk(self.path))]
623 625 walked_dir.sort()
624 626 for dirname, subdirs, fnames in walked_dir:
625 627 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
626 628 return '\n'.join(result_lines)
627 629
628 630
629 631 class Code(TextDisplayObject):
630 632 """Display syntax-highlighted source code.
631 633
632 634 This uses Pygments to highlight the code for HTML and Latex output.
633 635
634 636 Parameters
635 637 ----------
636 638 data : str
637 639 The code as a string
638 640 url : str
639 641 A URL to fetch the code from
640 642 filename : str
641 643 A local filename to load the code from
642 644 language : str
643 645 The short name of a Pygments lexer to use for highlighting.
644 646 If not specified, it will guess the lexer based on the filename
645 647 or the code. Available lexers: http://pygments.org/docs/lexers/
646 648 """
647 649 def __init__(self, data=None, url=None, filename=None, language=None):
648 650 self.language = language
649 651 super().__init__(data=data, url=url, filename=filename)
650 652
651 653 def _get_lexer(self):
652 654 if self.language:
653 655 from pygments.lexers import get_lexer_by_name
654 656 return get_lexer_by_name(self.language)
655 657 elif self.filename:
656 658 from pygments.lexers import get_lexer_for_filename
657 659 return get_lexer_for_filename(self.filename)
658 660 else:
659 661 from pygments.lexers import guess_lexer
660 662 return guess_lexer(self.data)
661 663
662 664 def __repr__(self):
663 665 return self.data
664 666
665 667 def _repr_html_(self):
666 668 from pygments import highlight
667 669 from pygments.formatters import HtmlFormatter
668 670 fmt = HtmlFormatter()
669 671 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
670 672 return style + highlight(self.data, self._get_lexer(), fmt)
671 673
672 674 def _repr_latex_(self):
673 675 from pygments import highlight
674 676 from pygments.formatters import LatexFormatter
675 677 return highlight(self.data, self._get_lexer(), LatexFormatter())
@@ -1,46 +1,48 b''
1 1 # encoding: utf-8
2 2 """
3 3 Timezone utilities
4 4
5 5 Just UTC-awareness right now
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2013 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 from datetime import tzinfo, timedelta, datetime
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Code
23 23 #-----------------------------------------------------------------------------
24 24 # constant for zero offset
25 25 ZERO = timedelta(0)
26 26
27 27 class tzUTC(tzinfo):
28 28 """tzinfo object for UTC (zero offset)"""
29 29
30 30 def utcoffset(self, d):
31 31 return ZERO
32 32
33 33 def dst(self, d):
34 34 return ZERO
35 35
36 UTC = tzUTC()
36
37 UTC = tzUTC() # type: ignore[abstract]
38
37 39
38 40 def utc_aware(unaware):
39 41 """decorator for adding UTC tzinfo to datetime's utcfoo methods"""
40 42 def utc_method(*args, **kwargs):
41 43 dt = unaware(*args, **kwargs)
42 44 return dt.replace(tzinfo=UTC)
43 45 return utc_method
44 46
45 47 utcfromtimestamp = utc_aware(datetime.utcfromtimestamp)
46 48 utcnow = utc_aware(datetime.utcnow)
@@ -1,3 +1,32 b''
1 1 [build-system]
2 2 requires = ["setuptools >= 51.0.0"]
3 3 build-backend = "setuptools.build_meta"
4 [tool.mypy]
5 python_version = 3.8
6 ignore_missing_imports = true
7 follow_imports = 'silent'
8 exclude = [
9 'test_\.+\.py',
10 'IPython.utils.tests.test_wildcard',
11 'testing',
12 'tests',
13 'PyColorize.py',
14 '_process_win32_controller.py',
15 'IPython/core/application.py',
16 'IPython/core/completerlib.py',
17 'IPython/core/displaypub.py',
18 'IPython/core/historyapp.py',
19 #'IPython/core/interactiveshell.py',
20 'IPython/core/magic.py',
21 'IPython/core/profileapp.py',
22 'IPython/core/ultratb.py',
23 'IPython/lib/deepreload.py',
24 'IPython/lib/pretty.py',
25 'IPython/sphinxext/ipython_directive.py',
26 'IPython/terminal/ipapp.py',
27 'IPython/utils/_process_win32.py',
28 'IPython/utils/path.py',
29 'IPython/utils/timing.py',
30 'IPython/utils/text.py'
31 ]
32
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now