Type-annotate ExceptionInfo

This commit is contained in:
Ran Benita 2019-07-10 12:30:29 +03:00
parent 2dca68b863
commit 55a570e513
1 changed files with 50 additions and 25 deletions

View File

@ -5,6 +5,11 @@ import traceback
from inspect import CO_VARARGS from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS from inspect import CO_VARKEYWORDS
from traceback import format_exception_only from traceback import format_exception_only
from types import TracebackType
from typing import Optional
from typing import Pattern
from typing import Tuple
from typing import Union
from weakref import ref from weakref import ref
import attr import attr
@ -15,6 +20,9 @@ import _pytest
from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
if False: # TYPE_CHECKING
from typing import Type
class Code: class Code:
""" wrapper around Python code objects """ """ wrapper around Python code objects """
@ -379,12 +387,14 @@ class ExceptionInfo:
_assert_start_repr = "AssertionError('assert " _assert_start_repr = "AssertionError('assert "
_excinfo = attr.ib() _excinfo = attr.ib(
_striptext = attr.ib(default="") type=Optional[Tuple["Type[BaseException]", BaseException, TracebackType]]
_traceback = attr.ib(default=None) )
_striptext = attr.ib(type=str, default="")
_traceback = attr.ib(type=Optional[Traceback], default=None)
@classmethod @classmethod
def from_current(cls, exprinfo=None): def from_current(cls, exprinfo: Optional[str] = None) -> "ExceptionInfo":
"""returns an ExceptionInfo matching the current traceback """returns an ExceptionInfo matching the current traceback
.. warning:: .. warning::
@ -396,8 +406,11 @@ class ExceptionInfo:
strip ``AssertionError`` from the output, defaults strip ``AssertionError`` from the output, defaults
to the exception message/``__str__()`` to the exception message/``__str__()``
""" """
tup = sys.exc_info() tup_ = sys.exc_info()
assert tup[0] is not None, "no current exception" assert tup_[0] is not None, "no current exception"
assert tup_[1] is not None, "no current exception"
assert tup_[2] is not None, "no current exception"
tup = (tup_[0], tup_[1], tup_[2])
_striptext = "" _striptext = ""
if exprinfo is None and isinstance(tup[1], AssertionError): if exprinfo is None and isinstance(tup[1], AssertionError):
exprinfo = getattr(tup[1], "msg", None) exprinfo = getattr(tup[1], "msg", None)
@ -409,48 +422,60 @@ class ExceptionInfo:
return cls(tup, _striptext) return cls(tup, _striptext)
@classmethod @classmethod
def for_later(cls): def for_later(cls) -> "ExceptionInfo":
"""return an unfilled ExceptionInfo """return an unfilled ExceptionInfo
""" """
return cls(None) return cls(None)
@property @property
def type(self): def type(self) -> "Type[BaseException]":
"""the exception class""" """the exception class"""
assert (
self._excinfo is not None
), ".type can only be used after the context manager exits"
return self._excinfo[0] return self._excinfo[0]
@property @property
def value(self): def value(self) -> BaseException:
"""the exception value""" """the exception value"""
assert (
self._excinfo is not None
), ".value can only be used after the context manager exits"
return self._excinfo[1] return self._excinfo[1]
@property @property
def tb(self): def tb(self) -> TracebackType:
"""the exception raw traceback""" """the exception raw traceback"""
assert (
self._excinfo is not None
), ".tb can only be used after the context manager exits"
return self._excinfo[2] return self._excinfo[2]
@property @property
def typename(self): def typename(self) -> str:
"""the type name of the exception""" """the type name of the exception"""
assert (
self._excinfo is not None
), ".typename can only be used after the context manager exits"
return self.type.__name__ return self.type.__name__
@property @property
def traceback(self): def traceback(self) -> Traceback:
"""the traceback""" """the traceback"""
if self._traceback is None: if self._traceback is None:
self._traceback = Traceback(self.tb, excinfo=ref(self)) self._traceback = Traceback(self.tb, excinfo=ref(self))
return self._traceback return self._traceback
@traceback.setter @traceback.setter
def traceback(self, value): def traceback(self, value: Traceback) -> None:
self._traceback = value self._traceback = value
def __repr__(self): def __repr__(self) -> str:
if self._excinfo is None: if self._excinfo is None:
return "<ExceptionInfo for raises contextmanager>" return "<ExceptionInfo for raises contextmanager>"
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
def exconly(self, tryshort=False): def exconly(self, tryshort: bool = False) -> str:
""" return the exception as a string """ return the exception as a string
when 'tryshort' resolves to True, and the exception is a when 'tryshort' resolves to True, and the exception is a
@ -466,11 +491,11 @@ class ExceptionInfo:
text = text[len(self._striptext) :] text = text[len(self._striptext) :]
return text return text
def errisinstance(self, exc): def errisinstance(self, exc: "Type[BaseException]") -> bool:
""" return True if the exception is an instance of exc """ """ return True if the exception is an instance of exc """
return isinstance(self.value, exc) return isinstance(self.value, exc)
def _getreprcrash(self): def _getreprcrash(self) -> "ReprFileLocation":
exconly = self.exconly(tryshort=True) exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry() entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno path, lineno = entry.frame.code.raw.co_filename, entry.lineno
@ -478,13 +503,13 @@ class ExceptionInfo:
def getrepr( def getrepr(
self, self,
showlocals=False, showlocals: bool = False,
style="long", style: str = "long",
abspath=False, abspath: bool = False,
tbfilter=True, tbfilter: bool = True,
funcargs=False, funcargs: bool = False,
truncate_locals=True, truncate_locals: bool = True,
chain=True, chain: bool = True,
): ):
""" """
Return str()able representation of this exception info. Return str()able representation of this exception info.
@ -535,7 +560,7 @@ class ExceptionInfo:
) )
return fmt.repr_excinfo(self) return fmt.repr_excinfo(self)
def match(self, regexp): def match(self, regexp: Union[str, Pattern]) -> bool:
""" """
Check whether the regular expression 'regexp' is found in the string Check whether the regular expression 'regexp' is found in the string
representation of the exception using ``re.search``. If it matches representation of the exception using ``re.search``. If it matches