code/code: type annotations & doc cleanups
This commit is contained in:
parent
087b047426
commit
77f3cb4baa
|
@ -51,7 +51,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Code:
|
||||
""" wrapper around Python code objects """
|
||||
"""Wrapper around Python code objects."""
|
||||
|
||||
def __init__(self, rawcode) -> None:
|
||||
if not hasattr(rawcode, "co_filename"):
|
||||
|
@ -74,7 +74,7 @@ class Code:
|
|||
|
||||
@property
|
||||
def path(self) -> Union[py.path.local, str]:
|
||||
""" return a path object pointing to source code (or a str in case
|
||||
"""Return a path object pointing to source code (or a str in case
|
||||
of OSError / non-existing file).
|
||||
"""
|
||||
if not self.raw.co_filename:
|
||||
|
@ -92,24 +92,22 @@ class Code:
|
|||
|
||||
@property
|
||||
def fullsource(self) -> Optional["Source"]:
|
||||
""" return a _pytest._code.Source object for the full source file of the code
|
||||
"""
|
||||
"""Return a _pytest._code.Source object for the full source file of the code."""
|
||||
full, _ = findsource(self.raw)
|
||||
return full
|
||||
|
||||
def source(self) -> "Source":
|
||||
""" return a _pytest._code.Source object for the code object's source only
|
||||
"""
|
||||
"""Return a _pytest._code.Source object for the code object's source only."""
|
||||
# return source only for that part of code
|
||||
return Source(self.raw)
|
||||
|
||||
def getargs(self, var: bool = False) -> Tuple[str, ...]:
|
||||
""" return a tuple with the argument names for the code object
|
||||
"""Return a tuple with the argument names for the code object.
|
||||
|
||||
if 'var' is set True also return the names of the variable and
|
||||
keyword arguments when present
|
||||
If 'var' is set True also return the names of the variable and
|
||||
keyword arguments when present.
|
||||
"""
|
||||
# handfull shortcut for getting args
|
||||
# Handy shortcut for getting args.
|
||||
raw = self.raw
|
||||
argcount = raw.co_argcount
|
||||
if var:
|
||||
|
@ -131,44 +129,43 @@ class Frame:
|
|||
|
||||
@property
|
||||
def statement(self) -> "Source":
|
||||
""" statement this frame is at """
|
||||
"""Statement this frame is at."""
|
||||
if self.code.fullsource is None:
|
||||
return Source("")
|
||||
return self.code.fullsource.getstatement(self.lineno)
|
||||
|
||||
def eval(self, code, **vars):
|
||||
""" evaluate 'code' in the frame
|
||||
"""Evaluate 'code' in the frame.
|
||||
|
||||
'vars' are optional additional local variables
|
||||
'vars' are optional additional local variables.
|
||||
|
||||
returns the result of the evaluation
|
||||
Returns the result of the evaluation.
|
||||
"""
|
||||
f_locals = self.f_locals.copy()
|
||||
f_locals.update(vars)
|
||||
return eval(code, self.f_globals, f_locals)
|
||||
|
||||
def exec_(self, code, **vars) -> None:
|
||||
""" exec 'code' in the frame
|
||||
"""Exec 'code' in the frame.
|
||||
|
||||
'vars' are optional; additional local variables
|
||||
'vars' are optional; additional local variables.
|
||||
"""
|
||||
f_locals = self.f_locals.copy()
|
||||
f_locals.update(vars)
|
||||
exec(code, self.f_globals, f_locals)
|
||||
|
||||
def repr(self, object: object) -> str:
|
||||
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
|
||||
"""
|
||||
"""Return a 'safe' (non-recursive, one-line) string repr for 'object'."""
|
||||
return saferepr(object)
|
||||
|
||||
def is_true(self, object):
|
||||
return object
|
||||
|
||||
def getargs(self, var: bool = False):
|
||||
""" return a list of tuples (name, value) for all arguments
|
||||
"""Return a list of tuples (name, value) for all arguments.
|
||||
|
||||
if 'var' is set True also include the variable and keyword
|
||||
arguments when present
|
||||
If 'var' is set True, also include the variable and keyword arguments
|
||||
when present.
|
||||
"""
|
||||
retval = []
|
||||
for arg in self.code.getargs(var):
|
||||
|
@ -180,12 +177,16 @@ class Frame:
|
|||
|
||||
|
||||
class TracebackEntry:
|
||||
""" a single entry in a traceback """
|
||||
"""A single entry in a Traceback."""
|
||||
|
||||
_repr_style = None # type: Optional[Literal["short", "long"]]
|
||||
exprinfo = None
|
||||
|
||||
def __init__(self, rawentry: TracebackType, excinfo=None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
rawentry: TracebackType,
|
||||
excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
|
||||
) -> None:
|
||||
self._excinfo = excinfo
|
||||
self._rawentry = rawentry
|
||||
self.lineno = rawentry.tb_lineno - 1
|
||||
|
@ -207,26 +208,26 @@ class TracebackEntry:
|
|||
|
||||
@property
|
||||
def statement(self) -> "Source":
|
||||
""" _pytest._code.Source object for the current statement """
|
||||
"""_pytest._code.Source object for the current statement."""
|
||||
source = self.frame.code.fullsource
|
||||
assert source is not None
|
||||
return source.getstatement(self.lineno)
|
||||
|
||||
@property
|
||||
def path(self) -> Union[py.path.local, str]:
|
||||
""" path to the source code """
|
||||
"""Path to the source code."""
|
||||
return self.frame.code.path
|
||||
|
||||
@property
|
||||
def locals(self) -> Dict[str, Any]:
|
||||
""" locals of underlying frame """
|
||||
"""Locals of underlying frame."""
|
||||
return self.frame.f_locals
|
||||
|
||||
def getfirstlinesource(self) -> int:
|
||||
return self.frame.code.firstlineno
|
||||
|
||||
def getsource(self, astcache=None) -> Optional["Source"]:
|
||||
""" return failing source code. """
|
||||
"""Return failing source code."""
|
||||
# we use the passed in astcache to not reparse asttrees
|
||||
# within exception info printing
|
||||
source = self.frame.code.fullsource
|
||||
|
@ -251,19 +252,19 @@ class TracebackEntry:
|
|||
|
||||
source = property(getsource)
|
||||
|
||||
def ishidden(self):
|
||||
""" return True if the current frame has a var __tracebackhide__
|
||||
resolving to True.
|
||||
def ishidden(self) -> bool:
|
||||
"""Return True if the current frame has a var __tracebackhide__
|
||||
resolving to True.
|
||||
|
||||
If __tracebackhide__ is a callable, it gets called with the
|
||||
ExceptionInfo instance and can decide whether to hide the traceback.
|
||||
If __tracebackhide__ is a callable, it gets called with the
|
||||
ExceptionInfo instance and can decide whether to hide the traceback.
|
||||
|
||||
mostly for internal use
|
||||
Mostly for internal use.
|
||||
"""
|
||||
f = self.frame
|
||||
tbh = f.f_locals.get(
|
||||
"__tracebackhide__", f.f_globals.get("__tracebackhide__", False)
|
||||
)
|
||||
) # type: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]]
|
||||
if tbh and callable(tbh):
|
||||
return tbh(None if self._excinfo is None else self._excinfo())
|
||||
return tbh
|
||||
|
@ -280,21 +281,19 @@ class TracebackEntry:
|
|||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""" co_name of underlying code """
|
||||
"""co_name of underlying code."""
|
||||
return self.frame.code.raw.co_name
|
||||
|
||||
|
||||
class Traceback(List[TracebackEntry]):
|
||||
""" Traceback objects encapsulate and offer higher level
|
||||
access to Traceback entries.
|
||||
"""
|
||||
"""Traceback objects encapsulate and offer higher level access to Traceback entries."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tb: Union[TracebackType, Iterable[TracebackEntry]],
|
||||
excinfo: Optional["ReferenceType[ExceptionInfo]"] = None,
|
||||
excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
|
||||
) -> None:
|
||||
""" initialize from given python traceback object and ExceptionInfo """
|
||||
"""Initialize from given python traceback object and ExceptionInfo."""
|
||||
self._excinfo = excinfo
|
||||
if isinstance(tb, TracebackType):
|
||||
|
||||
|
@ -313,16 +312,16 @@ class Traceback(List[TracebackEntry]):
|
|||
path=None,
|
||||
lineno: Optional[int] = None,
|
||||
firstlineno: Optional[int] = None,
|
||||
excludepath=None,
|
||||
excludepath: Optional[py.path.local] = None,
|
||||
) -> "Traceback":
|
||||
""" return a Traceback instance wrapping part of this Traceback
|
||||
"""Return a Traceback instance wrapping part of this Traceback.
|
||||
|
||||
by providing any combination of path, lineno and firstlineno, the
|
||||
first frame to start the to-be-returned traceback is determined
|
||||
By providing any combination of path, lineno and firstlineno, the
|
||||
first frame to start the to-be-returned traceback is determined.
|
||||
|
||||
this allows cutting the first part of a Traceback instance e.g.
|
||||
for formatting reasons (removing some uninteresting bits that deal
|
||||
with handling of the exception/traceback)
|
||||
This allows cutting the first part of a Traceback instance e.g.
|
||||
for formatting reasons (removing some uninteresting bits that deal
|
||||
with handling of the exception/traceback).
|
||||
"""
|
||||
for x in self:
|
||||
code = x.frame.code
|
||||
|
@ -359,21 +358,19 @@ class Traceback(List[TracebackEntry]):
|
|||
def filter(
|
||||
self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden()
|
||||
) -> "Traceback":
|
||||
""" return a Traceback instance with certain items removed
|
||||
"""Return a Traceback instance with certain items removed
|
||||
|
||||
fn is a function that gets a single argument, a TracebackEntry
|
||||
instance, and should return True when the item should be added
|
||||
to the Traceback, False when not
|
||||
fn is a function that gets a single argument, a TracebackEntry
|
||||
instance, and should return True when the item should be added
|
||||
to the Traceback, False when not.
|
||||
|
||||
by default this removes all the TracebackEntries which are hidden
|
||||
(see ishidden() above)
|
||||
By default this removes all the TracebackEntries which are hidden
|
||||
(see ishidden() above).
|
||||
"""
|
||||
return Traceback(filter(fn, self), self._excinfo)
|
||||
|
||||
def getcrashentry(self) -> TracebackEntry:
|
||||
""" return last non-hidden traceback entry that lead
|
||||
to the exception of a traceback.
|
||||
"""
|
||||
"""Return last non-hidden traceback entry that lead to the exception of a traceback."""
|
||||
for i in range(-1, -len(self) - 1, -1):
|
||||
entry = self[i]
|
||||
if not entry.ishidden():
|
||||
|
@ -381,9 +378,8 @@ class Traceback(List[TracebackEntry]):
|
|||
return self[-1]
|
||||
|
||||
def recursionindex(self) -> Optional[int]:
|
||||
""" return the index of the frame/TracebackEntry where recursion
|
||||
originates if appropriate, None if no recursion occurred
|
||||
"""
|
||||
"""Return the index of the frame/TracebackEntry where recursion originates if
|
||||
appropriate, None if no recursion occurred."""
|
||||
cache = {} # type: Dict[Tuple[Any, int, int], List[Dict[str, Any]]]
|
||||
for i, entry in enumerate(self):
|
||||
# id for the code.raw is needed to work around
|
||||
|
@ -414,14 +410,12 @@ co_equal = compile(
|
|||
)
|
||||
|
||||
|
||||
_E = TypeVar("_E", bound=BaseException)
|
||||
_E = TypeVar("_E", bound=BaseException, covariant=True)
|
||||
|
||||
|
||||
@attr.s(repr=False)
|
||||
class ExceptionInfo(Generic[_E]):
|
||||
""" wraps sys.exc_info() objects and offers
|
||||
help for navigating the traceback.
|
||||
"""
|
||||
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
|
||||
|
||||
_assert_start_repr = "AssertionError('assert "
|
||||
|
||||
|
@ -435,13 +429,12 @@ class ExceptionInfo(Generic[_E]):
|
|||
exc_info: Tuple["Type[_E]", "_E", TracebackType],
|
||||
exprinfo: Optional[str] = None,
|
||||
) -> "ExceptionInfo[_E]":
|
||||
"""returns an ExceptionInfo for an existing exc_info tuple.
|
||||
"""Returns an ExceptionInfo for an existing exc_info tuple.
|
||||
|
||||
.. warning::
|
||||
|
||||
Experimental API
|
||||
|
||||
|
||||
:param exprinfo: a text string helping to determine if we should
|
||||
strip ``AssertionError`` from the output, defaults
|
||||
to the exception message/``__str__()``
|
||||
|
@ -460,13 +453,12 @@ class ExceptionInfo(Generic[_E]):
|
|||
def from_current(
|
||||
cls, exprinfo: Optional[str] = None
|
||||
) -> "ExceptionInfo[BaseException]":
|
||||
"""returns an ExceptionInfo matching the current traceback
|
||||
"""Returns an ExceptionInfo matching the current traceback.
|
||||
|
||||
.. warning::
|
||||
|
||||
Experimental API
|
||||
|
||||
|
||||
:param exprinfo: a text string helping to determine if we should
|
||||
strip ``AssertionError`` from the output, defaults
|
||||
to the exception message/``__str__()``
|
||||
|
@ -480,8 +472,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@classmethod
|
||||
def for_later(cls) -> "ExceptionInfo[_E]":
|
||||
"""return an unfilled ExceptionInfo
|
||||
"""
|
||||
"""Return an unfilled ExceptionInfo."""
|
||||
return cls(None)
|
||||
|
||||
def fill_unfilled(self, exc_info: Tuple["Type[_E]", _E, TracebackType]) -> None:
|
||||
|
@ -491,7 +482,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@property
|
||||
def type(self) -> "Type[_E]":
|
||||
"""the exception class"""
|
||||
"""The exception class."""
|
||||
assert (
|
||||
self._excinfo is not None
|
||||
), ".type can only be used after the context manager exits"
|
||||
|
@ -499,7 +490,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@property
|
||||
def value(self) -> _E:
|
||||
"""the exception value"""
|
||||
"""The exception value."""
|
||||
assert (
|
||||
self._excinfo is not None
|
||||
), ".value can only be used after the context manager exits"
|
||||
|
@ -507,7 +498,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@property
|
||||
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"
|
||||
|
@ -515,7 +506,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@property
|
||||
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"
|
||||
|
@ -523,7 +514,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@property
|
||||
def traceback(self) -> Traceback:
|
||||
"""the traceback"""
|
||||
"""The traceback."""
|
||||
if self._traceback is None:
|
||||
self._traceback = Traceback(self.tb, excinfo=ref(self))
|
||||
return self._traceback
|
||||
|
@ -540,12 +531,12 @@ class ExceptionInfo(Generic[_E]):
|
|||
)
|
||||
|
||||
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
|
||||
_pytest._code._AssertionError, only the actual exception part of
|
||||
the exception representation is returned (so 'AssertionError: ' is
|
||||
removed from the beginning)
|
||||
When 'tryshort' resolves to True, and the exception is a
|
||||
_pytest._code._AssertionError, only the actual exception part of
|
||||
the exception representation is returned (so 'AssertionError: ' is
|
||||
removed from the beginning).
|
||||
"""
|
||||
lines = format_exception_only(self.type, self.value)
|
||||
text = "".join(lines)
|
||||
|
@ -580,8 +571,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
truncate_locals: bool = True,
|
||||
chain: bool = True,
|
||||
) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]:
|
||||
"""
|
||||
Return str()able representation of this exception info.
|
||||
"""Return str()able representation of this exception info.
|
||||
|
||||
:param bool showlocals:
|
||||
Show locals per traceback entry.
|
||||
|
@ -630,11 +620,10 @@ class ExceptionInfo(Generic[_E]):
|
|||
return fmt.repr_excinfo(self)
|
||||
|
||||
def match(self, regexp: "Union[str, Pattern]") -> "Literal[True]":
|
||||
"""
|
||||
Check whether the regular expression `regexp` matches the string
|
||||
"""Check whether the regular expression `regexp` matches the string
|
||||
representation of the exception using :func:`python:re.search`.
|
||||
If it matches `True` is returned.
|
||||
If it doesn't match an `AssertionError` is raised.
|
||||
|
||||
If it matches `True` is returned, otherwise an `AssertionError` is raised.
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
assert re.search(
|
||||
|
@ -646,7 +635,7 @@ class ExceptionInfo(Generic[_E]):
|
|||
|
||||
@attr.s
|
||||
class FormattedExcinfo:
|
||||
""" presenting information about failing Functions and Generators. """
|
||||
"""Presenting information about failing Functions and Generators."""
|
||||
|
||||
# for traceback entries
|
||||
flow_marker = ">"
|
||||
|
@ -697,7 +686,7 @@ class FormattedExcinfo:
|
|||
excinfo: Optional[ExceptionInfo] = None,
|
||||
short: bool = False,
|
||||
) -> List[str]:
|
||||
""" return formatted and marked up source lines. """
|
||||
"""Return formatted and marked up source lines."""
|
||||
lines = []
|
||||
if source is None or line_index >= len(source.lines):
|
||||
source = Source("???")
|
||||
|
@ -938,7 +927,7 @@ class ExceptionRepr(TerminalRepr):
|
|||
reprcrash = None # type: Optional[ReprFileLocation]
|
||||
reprtraceback = None # type: ReprTraceback
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
def __attrs_post_init__(self) -> None:
|
||||
self.sections = [] # type: List[Tuple[str, str, str]]
|
||||
|
||||
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
||||
|
@ -958,7 +947,7 @@ class ExceptionChainRepr(ExceptionRepr):
|
|||
]
|
||||
)
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
def __attrs_post_init__(self) -> None:
|
||||
super().__attrs_post_init__()
|
||||
# reprcrash and reprtraceback of the outermost (the newest) exception
|
||||
# in the chain
|
||||
|
@ -1160,8 +1149,9 @@ class ReprFuncArgs(TerminalRepr):
|
|||
tw.line("")
|
||||
|
||||
|
||||
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
|
||||
""" Return source location (path, lineno) for the given object.
|
||||
def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
|
||||
"""Return source location (path, lineno) for the given object.
|
||||
|
||||
If the source cannot be determined return ("", -1).
|
||||
|
||||
The line number is 0-based.
|
||||
|
@ -1171,13 +1161,13 @@ def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
|
|||
# in 6ec13a2b9. It ("place_as") appears to be something very custom.
|
||||
obj = get_real_func(obj)
|
||||
if hasattr(obj, "place_as"):
|
||||
obj = obj.place_as
|
||||
obj = obj.place_as # type: ignore[attr-defined]
|
||||
|
||||
try:
|
||||
code = Code(obj)
|
||||
except TypeError:
|
||||
try:
|
||||
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
|
||||
fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type]
|
||||
except TypeError:
|
||||
return "", -1
|
||||
|
||||
|
@ -1189,8 +1179,8 @@ def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
|
|||
except OSError:
|
||||
pass
|
||||
return fspath, lineno
|
||||
else:
|
||||
return code.path, code.firstlineno
|
||||
|
||||
return code.path, code.firstlineno
|
||||
|
||||
|
||||
# relative paths that we use to filter traceback entries from appearing to the user;
|
||||
|
|
Loading…
Reference in New Issue