Skip to content

Commit 55a570e

Browse files
committed
Type-annotate ExceptionInfo
1 parent 2dca68b commit 55a570e

File tree

1 file changed

+50
-25
lines changed

1 file changed

+50
-25
lines changed

src/_pytest/_code/code.py

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
from inspect import CO_VARARGS
66
from inspect import CO_VARKEYWORDS
77
from traceback import format_exception_only
8+
from types import TracebackType
9+
from typing import Optional
10+
from typing import Pattern
11+
from typing import Tuple
12+
from typing import Union
813
from weakref import ref
914

1015
import attr
@@ -15,6 +20,9 @@
1520
from _pytest._io.saferepr import safeformat
1621
from _pytest._io.saferepr import saferepr
1722

23+
if False: # TYPE_CHECKING
24+
from typing import Type
25+
1826

1927
class Code:
2028
""" wrapper around Python code objects """
@@ -379,12 +387,14 @@ class ExceptionInfo:
379387

380388
_assert_start_repr = "AssertionError('assert "
381389

382-
_excinfo = attr.ib()
383-
_striptext = attr.ib(default="")
384-
_traceback = attr.ib(default=None)
390+
_excinfo = attr.ib(
391+
type=Optional[Tuple["Type[BaseException]", BaseException, TracebackType]]
392+
)
393+
_striptext = attr.ib(type=str, default="")
394+
_traceback = attr.ib(type=Optional[Traceback], default=None)
385395

386396
@classmethod
387-
def from_current(cls, exprinfo=None):
397+
def from_current(cls, exprinfo: Optional[str] = None) -> "ExceptionInfo":
388398
"""returns an ExceptionInfo matching the current traceback
389399
390400
.. warning::
@@ -396,8 +406,11 @@ def from_current(cls, exprinfo=None):
396406
strip ``AssertionError`` from the output, defaults
397407
to the exception message/``__str__()``
398408
"""
399-
tup = sys.exc_info()
400-
assert tup[0] is not None, "no current exception"
409+
tup_ = sys.exc_info()
410+
assert tup_[0] is not None, "no current exception"
411+
assert tup_[1] is not None, "no current exception"
412+
assert tup_[2] is not None, "no current exception"
413+
tup = (tup_[0], tup_[1], tup_[2])
401414
_striptext = ""
402415
if exprinfo is None and isinstance(tup[1], AssertionError):
403416
exprinfo = getattr(tup[1], "msg", None)
@@ -409,48 +422,60 @@ def from_current(cls, exprinfo=None):
409422
return cls(tup, _striptext)
410423

411424
@classmethod
412-
def for_later(cls):
425+
def for_later(cls) -> "ExceptionInfo":
413426
"""return an unfilled ExceptionInfo
414427
"""
415428
return cls(None)
416429

417430
@property
418-
def type(self):
431+
def type(self) -> "Type[BaseException]":
419432
"""the exception class"""
433+
assert (
434+
self._excinfo is not None
435+
), ".type can only be used after the context manager exits"
420436
return self._excinfo[0]
421437

422438
@property
423-
def value(self):
439+
def value(self) -> BaseException:
424440
"""the exception value"""
441+
assert (
442+
self._excinfo is not None
443+
), ".value can only be used after the context manager exits"
425444
return self._excinfo[1]
426445

427446
@property
428-
def tb(self):
447+
def tb(self) -> TracebackType:
429448
"""the exception raw traceback"""
449+
assert (
450+
self._excinfo is not None
451+
), ".tb can only be used after the context manager exits"
430452
return self._excinfo[2]
431453

432454
@property
433-
def typename(self):
455+
def typename(self) -> str:
434456
"""the type name of the exception"""
457+
assert (
458+
self._excinfo is not None
459+
), ".typename can only be used after the context manager exits"
435460
return self.type.__name__
436461

437462
@property
438-
def traceback(self):
463+
def traceback(self) -> Traceback:
439464
"""the traceback"""
440465
if self._traceback is None:
441466
self._traceback = Traceback(self.tb, excinfo=ref(self))
442467
return self._traceback
443468

444469
@traceback.setter
445-
def traceback(self, value):
470+
def traceback(self, value: Traceback) -> None:
446471
self._traceback = value
447472

448-
def __repr__(self):
473+
def __repr__(self) -> str:
449474
if self._excinfo is None:
450475
return "<ExceptionInfo for raises contextmanager>"
451476
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
452477

453-
def exconly(self, tryshort=False):
478+
def exconly(self, tryshort: bool = False) -> str:
454479
""" return the exception as a string
455480
456481
when 'tryshort' resolves to True, and the exception is a
@@ -466,25 +491,25 @@ def exconly(self, tryshort=False):
466491
text = text[len(self._striptext) :]
467492
return text
468493

469-
def errisinstance(self, exc):
494+
def errisinstance(self, exc: "Type[BaseException]") -> bool:
470495
""" return True if the exception is an instance of exc """
471496
return isinstance(self.value, exc)
472497

473-
def _getreprcrash(self):
498+
def _getreprcrash(self) -> "ReprFileLocation":
474499
exconly = self.exconly(tryshort=True)
475500
entry = self.traceback.getcrashentry()
476501
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
477502
return ReprFileLocation(path, lineno + 1, exconly)
478503

479504
def getrepr(
480505
self,
481-
showlocals=False,
482-
style="long",
483-
abspath=False,
484-
tbfilter=True,
485-
funcargs=False,
486-
truncate_locals=True,
487-
chain=True,
506+
showlocals: bool = False,
507+
style: str = "long",
508+
abspath: bool = False,
509+
tbfilter: bool = True,
510+
funcargs: bool = False,
511+
truncate_locals: bool = True,
512+
chain: bool = True,
488513
):
489514
"""
490515
Return str()able representation of this exception info.
@@ -535,7 +560,7 @@ def getrepr(
535560
)
536561
return fmt.repr_excinfo(self)
537562

538-
def match(self, regexp):
563+
def match(self, regexp: Union[str, Pattern]) -> bool:
539564
"""
540565
Check whether the regular expression 'regexp' is found in the string
541566
representation of the exception using ``re.search``. If it matches

0 commit comments

Comments
 (0)