aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/_code
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-02-10 16:44:39 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:44:39 +0300
commite9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch)
tree64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/pytest/py3/_pytest/_code
parent2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff)
downloadydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/_code')
-rw-r--r--contrib/python/pytest/py3/_pytest/_code/__init__.py44
-rw-r--r--contrib/python/pytest/py3/_pytest/_code/code.py1490
-rw-r--r--contrib/python/pytest/py3/_pytest/_code/source.py192
3 files changed, 863 insertions, 863 deletions
diff --git a/contrib/python/pytest/py3/_pytest/_code/__init__.py b/contrib/python/pytest/py3/_pytest/_code/__init__.py
index 2f13335d10..511d0dde66 100644
--- a/contrib/python/pytest/py3/_pytest/_code/__init__.py
+++ b/contrib/python/pytest/py3/_pytest/_code/__init__.py
@@ -1,22 +1,22 @@
-"""Python inspection/code generation API."""
-from .code import Code
-from .code import ExceptionInfo
-from .code import filter_traceback
-from .code import Frame
-from .code import getfslineno
-from .code import Traceback
-from .code import TracebackEntry
-from .source import getrawcode
-from .source import Source
-
-__all__ = [
- "Code",
- "ExceptionInfo",
- "filter_traceback",
- "Frame",
- "getfslineno",
- "getrawcode",
- "Traceback",
- "TracebackEntry",
- "Source",
-]
+"""Python inspection/code generation API."""
+from .code import Code
+from .code import ExceptionInfo
+from .code import filter_traceback
+from .code import Frame
+from .code import getfslineno
+from .code import Traceback
+from .code import TracebackEntry
+from .source import getrawcode
+from .source import Source
+
+__all__ = [
+ "Code",
+ "ExceptionInfo",
+ "filter_traceback",
+ "Frame",
+ "getfslineno",
+ "getrawcode",
+ "Traceback",
+ "TracebackEntry",
+ "Source",
+]
diff --git a/contrib/python/pytest/py3/_pytest/_code/code.py b/contrib/python/pytest/py3/_pytest/_code/code.py
index 6e3e270b1c..423069330a 100644
--- a/contrib/python/pytest/py3/_pytest/_code/code.py
+++ b/contrib/python/pytest/py3/_pytest/_code/code.py
@@ -4,29 +4,29 @@ import sys
import traceback
from inspect import CO_VARARGS
from inspect import CO_VARKEYWORDS
-from io import StringIO
-from pathlib import Path
-from traceback import format_exception_only
-from types import CodeType
-from types import FrameType
-from types import TracebackType
-from typing import Any
-from typing import Callable
-from typing import Dict
-from typing import Generic
-from typing import Iterable
-from typing import List
-from typing import Mapping
-from typing import Optional
-from typing import overload
-from typing import Pattern
-from typing import Sequence
-from typing import Set
-from typing import Tuple
-from typing import Type
-from typing import TYPE_CHECKING
-from typing import TypeVar
-from typing import Union
+from io import StringIO
+from pathlib import Path
+from traceback import format_exception_only
+from types import CodeType
+from types import FrameType
+from types import TracebackType
+from typing import Any
+from typing import Callable
+from typing import Dict
+from typing import Generic
+from typing import Iterable
+from typing import List
+from typing import Mapping
+from typing import Optional
+from typing import overload
+from typing import Pattern
+from typing import Sequence
+from typing import Set
+from typing import Tuple
+from typing import Type
+from typing import TYPE_CHECKING
+from typing import TypeVar
+from typing import Union
from weakref import ref
import attr
@@ -34,84 +34,84 @@ import pluggy
import py
import _pytest
-from _pytest._code.source import findsource
-from _pytest._code.source import getrawcode
-from _pytest._code.source import getstatementrange_ast
-from _pytest._code.source import Source
-from _pytest._io import TerminalWriter
-from _pytest._io.saferepr import safeformat
-from _pytest._io.saferepr import saferepr
-from _pytest.compat import final
-from _pytest.compat import get_real_func
-
-if TYPE_CHECKING:
- from typing_extensions import Literal
- from weakref import ReferenceType
-
- _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
-
-
-class Code:
- """Wrapper around Python code objects."""
-
- __slots__ = ("raw",)
-
- def __init__(self, obj: CodeType) -> None:
- self.raw = obj
-
- @classmethod
- def from_function(cls, obj: object) -> "Code":
- return cls(getrawcode(obj))
-
+from _pytest._code.source import findsource
+from _pytest._code.source import getrawcode
+from _pytest._code.source import getstatementrange_ast
+from _pytest._code.source import Source
+from _pytest._io import TerminalWriter
+from _pytest._io.saferepr import safeformat
+from _pytest._io.saferepr import saferepr
+from _pytest.compat import final
+from _pytest.compat import get_real_func
+
+if TYPE_CHECKING:
+ from typing_extensions import Literal
+ from weakref import ReferenceType
+
+ _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
+
+
+class Code:
+ """Wrapper around Python code objects."""
+
+ __slots__ = ("raw",)
+
+ def __init__(self, obj: CodeType) -> None:
+ self.raw = obj
+
+ @classmethod
+ def from_function(cls, obj: object) -> "Code":
+ return cls(getrawcode(obj))
+
def __eq__(self, other):
return self.raw == other.raw
- # Ignore type because of https://github.com/python/mypy/issues/4266.
- __hash__ = None # type: ignore
+ # Ignore type because of https://github.com/python/mypy/issues/4266.
+ __hash__ = None # type: ignore
- @property
- def firstlineno(self) -> int:
- return self.raw.co_firstlineno - 1
+ @property
+ def firstlineno(self) -> int:
+ return self.raw.co_firstlineno - 1
+
+ @property
+ def name(self) -> str:
+ return self.raw.co_name
@property
- def name(self) -> str:
- return self.raw.co_name
-
- @property
- def path(self) -> Union[py.path.local, str]:
- """Return a path object pointing to source code, or an ``str`` in
- case of ``OSError`` / non-existing file."""
- if not self.raw.co_filename:
- return ""
+ def path(self) -> Union[py.path.local, str]:
+ """Return a path object pointing to source code, or an ``str`` in
+ case of ``OSError`` / non-existing file."""
+ if not self.raw.co_filename:
+ return ""
try:
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
raise OSError("py.path check failed.")
- return p
+ return p
except OSError:
# XXX maybe try harder like the weird logic
# in the standard lib [linecache.updatecache] does?
- return self.raw.co_filename
+ return self.raw.co_filename
@property
- def fullsource(self) -> Optional["Source"]:
- """Return a _pytest._code.Source object for the full source file of the code."""
- full, _ = findsource(self.raw)
+ def fullsource(self) -> Optional["Source"]:
+ """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."""
+ def source(self) -> "Source":
+ """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)
+ return Source(self.raw)
- def getargs(self, var: bool = False) -> Tuple[str, ...]:
- """Return a tuple with the argument names for the code object.
+ def getargs(self, var: bool = False) -> Tuple[str, ...]:
+ """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.
"""
- # Handy shortcut for getting args.
+ # Handy shortcut for getting args.
raw = self.raw
argcount = raw.co_argcount
if var:
@@ -120,58 +120,58 @@ class Code:
return raw.co_varnames[:argcount]
-class Frame:
+class Frame:
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
- __slots__ = ("raw",)
-
- def __init__(self, frame: FrameType) -> None:
+ __slots__ = ("raw",)
+
+ def __init__(self, frame: FrameType) -> None:
self.raw = frame
@property
- def lineno(self) -> int:
- return self.raw.f_lineno - 1
-
- @property
- def f_globals(self) -> Dict[str, Any]:
- return self.raw.f_globals
-
- @property
- def f_locals(self) -> Dict[str, Any]:
- return self.raw.f_locals
-
- @property
- def code(self) -> Code:
- return Code(self.raw.f_code)
-
- @property
- def statement(self) -> "Source":
- """Statement this frame is at."""
+ def lineno(self) -> int:
+ return self.raw.f_lineno - 1
+
+ @property
+ def f_globals(self) -> Dict[str, Any]:
+ return self.raw.f_globals
+
+ @property
+ def f_locals(self) -> Dict[str, Any]:
+ return self.raw.f_locals
+
+ @property
+ def code(self) -> Code:
+ return Code(self.raw.f_code)
+
+ @property
+ def statement(self) -> "Source":
+ """Statement this frame is at."""
if self.code.fullsource is None:
- return Source("")
+ 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 repr(self, object: object) -> str:
- """Return a 'safe' (non-recursive, one-line) string repr for 'object'."""
- return saferepr(object)
+ def repr(self, object: object) -> str:
+ """Return a 'safe' (non-recursive, one-line) string repr for 'object'."""
+ return saferepr(object)
- def getargs(self, var: bool = False):
- """Return a list of tuples (name, value) for all arguments.
+ def getargs(self, var: bool = False):
+ """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):
@@ -182,61 +182,61 @@ class Frame:
return retval
-class TracebackEntry:
- """A single entry in a Traceback."""
+class TracebackEntry:
+ """A single entry in a Traceback."""
- __slots__ = ("_rawentry", "_excinfo", "_repr_style")
+ __slots__ = ("_rawentry", "_excinfo", "_repr_style")
- def __init__(
- self,
- rawentry: TracebackType,
- excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
- ) -> None:
- self._rawentry = rawentry
+ def __init__(
+ self,
+ rawentry: TracebackType,
+ excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
+ ) -> None:
+ self._rawentry = rawentry
self._excinfo = excinfo
- self._repr_style: Optional['Literal["short", "long"]'] = None
+ self._repr_style: Optional['Literal["short", "long"]'] = None
+
+ @property
+ def lineno(self) -> int:
+ return self._rawentry.tb_lineno - 1
- @property
- def lineno(self) -> int:
- return self._rawentry.tb_lineno - 1
-
- def set_repr_style(self, mode: "Literal['short', 'long']") -> None:
+ def set_repr_style(self, mode: "Literal['short', 'long']") -> None:
assert mode in ("short", "long")
self._repr_style = mode
@property
- def frame(self) -> Frame:
- return Frame(self._rawentry.tb_frame)
+ def frame(self) -> Frame:
+ return Frame(self._rawentry.tb_frame)
@property
- def relline(self) -> int:
+ def relline(self) -> int:
return self.lineno - self.frame.code.firstlineno
- def __repr__(self) -> str:
+ def __repr__(self) -> str:
return "<TracebackEntry %s:%d>" % (self.frame.code.path, self.lineno + 1)
@property
- def statement(self) -> "Source":
- """_pytest._code.Source object for the current statement."""
+ def statement(self) -> "Source":
+ """_pytest._code.Source object for the current statement."""
source = self.frame.code.fullsource
- assert source is not None
+ assert source is not None
return source.getstatement(self.lineno)
@property
- def path(self) -> Union[py.path.local, str]:
- """Path to the source code."""
+ def path(self) -> Union[py.path.local, str]:
+ """Path to the source code."""
return self.frame.code.path
- @property
- def locals(self) -> Dict[str, Any]:
- """Locals of underlying frame."""
+ @property
+ def locals(self) -> Dict[str, Any]:
+ """Locals of underlying frame."""
return self.frame.f_locals
- def getfirstlinesource(self) -> int:
- return self.frame.code.firstlineno
+ def getfirstlinesource(self) -> int:
+ return self.frame.code.firstlineno
- def getsource(self, astcache=None) -> Optional["Source"]:
- """Return failing source code."""
+ def getsource(self, astcache=None) -> Optional["Source"]:
+ """Return failing source code."""
# we use the passed in astcache to not reparse asttrees
# within exception info printing
source = self.frame.code.fullsource
@@ -261,94 +261,94 @@ class TracebackEntry:
source = property(getsource)
- def ishidden(self) -> bool:
- """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.
"""
- tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = (
- False
- )
- for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals):
- # in normal cases, f_locals and f_globals are dictionaries
- # however via `exec(...)` / `eval(...)` they can be other types
- # (even incorrect types!).
- # as such, we suppress all exceptions while accessing __tracebackhide__
- try:
- tbh = maybe_ns_dct["__tracebackhide__"]
- except Exception:
- pass
- else:
- break
- if tbh and callable(tbh):
+ tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = (
+ False
+ )
+ for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals):
+ # in normal cases, f_locals and f_globals are dictionaries
+ # however via `exec(...)` / `eval(...)` they can be other types
+ # (even incorrect types!).
+ # as such, we suppress all exceptions while accessing __tracebackhide__
+ try:
+ tbh = maybe_ns_dct["__tracebackhide__"]
+ except Exception:
+ pass
+ else:
+ break
+ if tbh and callable(tbh):
return tbh(None if self._excinfo is None else self._excinfo())
- return tbh
+ return tbh
- def __str__(self) -> str:
+ def __str__(self) -> str:
name = self.frame.code.name
try:
line = str(self.statement).lstrip()
except KeyboardInterrupt:
raise
- except BaseException:
+ except BaseException:
line = "???"
- # This output does not quite match Python's repr for traceback entries,
- # but changing it to do so would break certain plugins. See
- # https://github.com/pytest-dev/pytest/pull/7535/ for details.
- return " File %r:%d in %s\n %s\n" % (
- str(self.path),
- self.lineno + 1,
- name,
- line,
- )
-
- @property
- def name(self) -> str:
- """co_name of underlying code."""
+ # This output does not quite match Python's repr for traceback entries,
+ # but changing it to do so would break certain plugins. See
+ # https://github.com/pytest-dev/pytest/pull/7535/ for details.
+ return " File %r:%d in %s\n %s\n" % (
+ str(self.path),
+ self.lineno + 1,
+ name,
+ line,
+ )
+
+ @property
+ def name(self) -> str:
+ """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."""
+class Traceback(List[TracebackEntry]):
+ """Traceback objects encapsulate and offer higher level access to Traceback entries."""
- def __init__(
- self,
- tb: Union[TracebackType, Iterable[TracebackEntry]],
- excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
- ) -> None:
- """Initialize from given python traceback object and ExceptionInfo."""
+ def __init__(
+ self,
+ tb: Union[TracebackType, Iterable[TracebackEntry]],
+ excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
+ ) -> None:
+ """Initialize from given python traceback object and ExceptionInfo."""
self._excinfo = excinfo
- if isinstance(tb, TracebackType):
+ if isinstance(tb, TracebackType):
- def f(cur: TracebackType) -> Iterable[TracebackEntry]:
- cur_: Optional[TracebackType] = cur
- while cur_ is not None:
- yield TracebackEntry(cur_, excinfo=excinfo)
- cur_ = cur_.tb_next
+ def f(cur: TracebackType) -> Iterable[TracebackEntry]:
+ cur_: Optional[TracebackType] = cur
+ while cur_ is not None:
+ yield TracebackEntry(cur_, excinfo=excinfo)
+ cur_ = cur_.tb_next
- super().__init__(f(tb))
+ super().__init__(f(tb))
else:
- super().__init__(tb)
-
- def cut(
- self,
- path=None,
- lineno: Optional[int] = None,
- firstlineno: Optional[int] = None,
- excludepath: Optional[py.path.local] = None,
- ) -> "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.
-
- 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).
+ super().__init__(tb)
+
+ def cut(
+ self,
+ path=None,
+ lineno: Optional[int] = None,
+ firstlineno: Optional[int] = None,
+ excludepath: Optional[py.path.local] = None,
+ ) -> "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.
+
+ 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
@@ -357,7 +357,7 @@ class Traceback(List[TracebackEntry]):
(path is None or codepath == path)
and (
excludepath is None
- or not isinstance(codepath, py.path.local)
+ or not isinstance(codepath, py.path.local)
or not codepath.relto(excludepath)
)
and (lineno is None or x.lineno == lineno)
@@ -366,46 +366,46 @@ class Traceback(List[TracebackEntry]):
return Traceback(x._rawentry, self._excinfo)
return self
- @overload
- def __getitem__(self, key: int) -> TracebackEntry:
- ...
-
- @overload
- def __getitem__(self, key: slice) -> "Traceback":
- ...
-
- def __getitem__(self, key: Union[int, slice]) -> Union[TracebackEntry, "Traceback"]:
- if isinstance(key, slice):
- return self.__class__(super().__getitem__(key))
- else:
- return super().__getitem__(key)
-
- def filter(
- self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden()
- ) -> "Traceback":
- """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.
-
- By default this removes all the TracebackEntries which are hidden
- (see ishidden() above).
+ @overload
+ def __getitem__(self, key: int) -> TracebackEntry:
+ ...
+
+ @overload
+ def __getitem__(self, key: slice) -> "Traceback":
+ ...
+
+ def __getitem__(self, key: Union[int, slice]) -> Union[TracebackEntry, "Traceback"]:
+ if isinstance(key, slice):
+ return self.__class__(super().__getitem__(key))
+ else:
+ return super().__getitem__(key)
+
+ def filter(
+ self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden()
+ ) -> "Traceback":
+ """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.
+
+ 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."""
+ def getcrashentry(self) -> TracebackEntry:
+ """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():
return entry
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."""
- cache: Dict[Tuple[Any, int, int], List[Dict[str, Any]]] = {}
+ def recursionindex(self) -> Optional[int]:
+ """Return the index of the frame/TracebackEntry where recursion originates if
+ appropriate, None if no recursion occurred."""
+ cache: 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
# the strange metaprogramming in the decorator lib from pypi
@@ -418,10 +418,10 @@ class Traceback(List[TracebackEntry]):
f = entry.frame
loc = f.f_locals
for otherloc in values:
- if f.eval(
- co_equal,
- __recursioncache_locals_1=loc,
- __recursioncache_locals_2=otherloc,
+ if f.eval(
+ co_equal,
+ __recursioncache_locals_1=loc,
+ __recursioncache_locals_2=otherloc,
):
return i
values.append(entry.frame.f_locals)
@@ -433,136 +433,136 @@ co_equal = compile(
)
-_E = TypeVar("_E", bound=BaseException, covariant=True)
-
-
-@final
-@attr.s(repr=False)
-class ExceptionInfo(Generic[_E]):
- """Wraps sys.exc_info() objects and offers help for navigating the traceback."""
-
- _assert_start_repr = "AssertionError('assert "
-
- _excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]])
- _striptext = attr.ib(type=str, default="")
- _traceback = attr.ib(type=Optional[Traceback], default=None)
-
- @classmethod
- def from_exc_info(
- cls,
- exc_info: Tuple[Type[_E], _E, TracebackType],
- exprinfo: Optional[str] = None,
- ) -> "ExceptionInfo[_E]":
- """Return 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__()``.
- """
- _striptext = ""
- if exprinfo is None and isinstance(exc_info[1], AssertionError):
- exprinfo = getattr(exc_info[1], "msg", None)
- if exprinfo is None:
- exprinfo = saferepr(exc_info[1])
- if exprinfo and exprinfo.startswith(cls._assert_start_repr):
- _striptext = "AssertionError: "
-
- return cls(exc_info, _striptext)
-
- @classmethod
- def from_current(
- cls, exprinfo: Optional[str] = None
- ) -> "ExceptionInfo[BaseException]":
- """Return 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__()``.
- """
- tup = sys.exc_info()
- 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"
- exc_info = (tup[0], tup[1], tup[2])
- return ExceptionInfo.from_exc_info(exc_info, exprinfo)
-
- @classmethod
- def for_later(cls) -> "ExceptionInfo[_E]":
- """Return an unfilled ExceptionInfo."""
- return cls(None)
-
- def fill_unfilled(self, exc_info: Tuple[Type[_E], _E, TracebackType]) -> None:
- """Fill an unfilled ExceptionInfo created with ``for_later()``."""
- assert self._excinfo is None, "ExceptionInfo was already filled"
- self._excinfo = exc_info
-
- @property
- def type(self) -> Type[_E]:
- """The exception class."""
- assert (
- self._excinfo is not None
- ), ".type can only be used after the context manager exits"
- return self._excinfo[0]
-
- @property
- def value(self) -> _E:
- """The exception value."""
- assert (
- self._excinfo is not None
- ), ".value can only be used after the context manager exits"
- return self._excinfo[1]
-
- @property
- def tb(self) -> TracebackType:
- """The exception raw traceback."""
- assert (
- self._excinfo is not None
- ), ".tb can only be used after the context manager exits"
- return self._excinfo[2]
-
- @property
- def typename(self) -> str:
- """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__
-
- @property
- def traceback(self) -> Traceback:
- """The traceback."""
- if self._traceback is None:
- self._traceback = Traceback(self.tb, excinfo=ref(self))
- return self._traceback
-
- @traceback.setter
- def traceback(self, value: Traceback) -> None:
- self._traceback = value
-
- def __repr__(self) -> str:
- if self._excinfo is None:
- return "<ExceptionInfo for raises contextmanager>"
- return "<{} {} tblen={}>".format(
- self.__class__.__name__, saferepr(self._excinfo[1]), len(self.traceback)
- )
-
- def exconly(self, tryshort: bool = False) -> str:
- """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).
+_E = TypeVar("_E", bound=BaseException, covariant=True)
+
+
+@final
+@attr.s(repr=False)
+class ExceptionInfo(Generic[_E]):
+ """Wraps sys.exc_info() objects and offers help for navigating the traceback."""
+
+ _assert_start_repr = "AssertionError('assert "
+
+ _excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]])
+ _striptext = attr.ib(type=str, default="")
+ _traceback = attr.ib(type=Optional[Traceback], default=None)
+
+ @classmethod
+ def from_exc_info(
+ cls,
+ exc_info: Tuple[Type[_E], _E, TracebackType],
+ exprinfo: Optional[str] = None,
+ ) -> "ExceptionInfo[_E]":
+ """Return 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__()``.
+ """
+ _striptext = ""
+ if exprinfo is None and isinstance(exc_info[1], AssertionError):
+ exprinfo = getattr(exc_info[1], "msg", None)
+ if exprinfo is None:
+ exprinfo = saferepr(exc_info[1])
+ if exprinfo and exprinfo.startswith(cls._assert_start_repr):
+ _striptext = "AssertionError: "
+
+ return cls(exc_info, _striptext)
+
+ @classmethod
+ def from_current(
+ cls, exprinfo: Optional[str] = None
+ ) -> "ExceptionInfo[BaseException]":
+ """Return 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__()``.
+ """
+ tup = sys.exc_info()
+ 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"
+ exc_info = (tup[0], tup[1], tup[2])
+ return ExceptionInfo.from_exc_info(exc_info, exprinfo)
+
+ @classmethod
+ def for_later(cls) -> "ExceptionInfo[_E]":
+ """Return an unfilled ExceptionInfo."""
+ return cls(None)
+
+ def fill_unfilled(self, exc_info: Tuple[Type[_E], _E, TracebackType]) -> None:
+ """Fill an unfilled ExceptionInfo created with ``for_later()``."""
+ assert self._excinfo is None, "ExceptionInfo was already filled"
+ self._excinfo = exc_info
+
+ @property
+ def type(self) -> Type[_E]:
+ """The exception class."""
+ assert (
+ self._excinfo is not None
+ ), ".type can only be used after the context manager exits"
+ return self._excinfo[0]
+
+ @property
+ def value(self) -> _E:
+ """The exception value."""
+ assert (
+ self._excinfo is not None
+ ), ".value can only be used after the context manager exits"
+ return self._excinfo[1]
+
+ @property
+ def tb(self) -> TracebackType:
+ """The exception raw traceback."""
+ assert (
+ self._excinfo is not None
+ ), ".tb can only be used after the context manager exits"
+ return self._excinfo[2]
+
+ @property
+ def typename(self) -> str:
+ """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__
+
+ @property
+ def traceback(self) -> Traceback:
+ """The traceback."""
+ if self._traceback is None:
+ self._traceback = Traceback(self.tb, excinfo=ref(self))
+ return self._traceback
+
+ @traceback.setter
+ def traceback(self, value: Traceback) -> None:
+ self._traceback = value
+
+ def __repr__(self) -> str:
+ if self._excinfo is None:
+ return "<ExceptionInfo for raises contextmanager>"
+ return "<{} {} tblen={}>".format(
+ self.__class__.__name__, saferepr(self._excinfo[1]), len(self.traceback)
+ )
+
+ def exconly(self, tryshort: bool = False) -> str:
+ """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).
"""
lines = format_exception_only(self.type, self.value)
text = "".join(lines)
@@ -572,16 +572,16 @@ class ExceptionInfo(Generic[_E]):
text = text[len(self._striptext) :]
return text
- def errisinstance(
- self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]]
- ) -> bool:
- """Return True if the exception is an instance of exc.
-
- Consider using ``isinstance(excinfo.value, exc)`` instead.
- """
+ def errisinstance(
+ self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]]
+ ) -> bool:
+ """Return True if the exception is an instance of exc.
+
+ Consider using ``isinstance(excinfo.value, exc)`` instead.
+ """
return isinstance(self.value, exc)
- def _getreprcrash(self) -> "ReprFileLocation":
+ def _getreprcrash(self) -> "ReprFileLocation":
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
@@ -589,22 +589,22 @@ class ExceptionInfo(Generic[_E]):
def getrepr(
self,
- showlocals: bool = False,
- style: "_TracebackStyle" = "long",
- abspath: bool = False,
- tbfilter: bool = True,
- funcargs: bool = False,
- truncate_locals: bool = True,
- chain: bool = True,
- ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]:
- """Return str()able representation of this exception info.
+ showlocals: bool = False,
+ style: "_TracebackStyle" = "long",
+ abspath: bool = False,
+ tbfilter: bool = True,
+ funcargs: bool = False,
+ truncate_locals: bool = True,
+ chain: bool = True,
+ ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]:
+ """Return str()able representation of this exception info.
:param bool showlocals:
Show locals per traceback entry.
Ignored if ``style=="native"``.
- :param str style:
- long|short|no|native|value traceback style.
+ :param str style:
+ long|short|no|native|value traceback style.
:param bool abspath:
If paths should be changed to absolute or left unchanged.
@@ -619,8 +619,8 @@ class ExceptionInfo(Generic[_E]):
:param bool truncate_locals:
With ``showlocals==True``, make sure locals can be safely represented as strings.
- :param bool chain:
- If chained exceptions in Python 3 should be shown.
+ :param bool chain:
+ If chained exceptions in Python 3 should be shown.
.. versionchanged:: 3.9
@@ -647,78 +647,78 @@ class ExceptionInfo(Generic[_E]):
)
return fmt.repr_excinfo(self)
- def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]":
- """Check whether the regular expression `regexp` matches the string
- representation of the exception using :func:`python:re.search`.
-
- If it matches `True` is returned, otherwise an `AssertionError` is raised.
+ def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]":
+ """Check whether the regular expression `regexp` matches the string
+ representation of the exception using :func:`python:re.search`.
+
+ If it matches `True` is returned, otherwise an `AssertionError` is raised.
"""
__tracebackhide__ = True
- msg = "Regex pattern {!r} does not match {!r}."
- if regexp == str(self.value):
- msg += " Did you mean to `re.escape()` the regex?"
- assert re.search(regexp, str(self.value)), msg.format(regexp, str(self.value))
- # Return True to allow for "assert excinfo.match()".
+ msg = "Regex pattern {!r} does not match {!r}."
+ if regexp == str(self.value):
+ msg += " Did you mean to `re.escape()` the regex?"
+ assert re.search(regexp, str(self.value)), msg.format(regexp, str(self.value))
+ # Return True to allow for "assert excinfo.match()".
return True
@attr.s
-class FormattedExcinfo:
- """Presenting information about failing Functions and Generators."""
+class FormattedExcinfo:
+ """Presenting information about failing Functions and Generators."""
# for traceback entries
flow_marker = ">"
fail_marker = "E"
- showlocals = attr.ib(type=bool, default=False)
- style = attr.ib(type="_TracebackStyle", default="long")
- abspath = attr.ib(type=bool, default=True)
- tbfilter = attr.ib(type=bool, default=True)
- funcargs = attr.ib(type=bool, default=False)
- truncate_locals = attr.ib(type=bool, default=True)
- chain = attr.ib(type=bool, default=True)
+ showlocals = attr.ib(type=bool, default=False)
+ style = attr.ib(type="_TracebackStyle", default="long")
+ abspath = attr.ib(type=bool, default=True)
+ tbfilter = attr.ib(type=bool, default=True)
+ funcargs = attr.ib(type=bool, default=False)
+ truncate_locals = attr.ib(type=bool, default=True)
+ chain = attr.ib(type=bool, default=True)
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
- def _getindent(self, source: "Source") -> int:
- # Figure out indent for the given source.
+ def _getindent(self, source: "Source") -> int:
+ # Figure out indent for the given source.
try:
s = str(source.getstatement(len(source) - 1))
except KeyboardInterrupt:
raise
- except BaseException:
+ except BaseException:
try:
s = str(source[-1])
except KeyboardInterrupt:
raise
- except BaseException:
+ except BaseException:
return 0
return 4 + (len(s) - len(s.lstrip()))
- def _getentrysource(self, entry: TracebackEntry) -> Optional["Source"]:
+ def _getentrysource(self, entry: TracebackEntry) -> Optional["Source"]:
source = entry.getsource(self.astcache)
if source is not None:
source = source.deindent()
return source
- def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]:
+ def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]:
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
- args.append((argname, saferepr(argvalue)))
+ args.append((argname, saferepr(argvalue)))
return ReprFuncArgs(args)
- return None
-
- def get_source(
- self,
- source: Optional["Source"],
- line_index: int = -1,
- excinfo: Optional[ExceptionInfo[BaseException]] = None,
- short: bool = False,
- ) -> List[str]:
- """Return formatted and marked up source lines."""
+ return None
+
+ def get_source(
+ self,
+ source: Optional["Source"],
+ line_index: int = -1,
+ excinfo: Optional[ExceptionInfo[BaseException]] = None,
+ short: bool = False,
+ ) -> List[str]:
+ """Return formatted and marked up source lines."""
lines = []
if source is None or line_index >= len(source.lines):
- source = Source("???")
+ source = Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
@@ -736,24 +736,24 @@ class FormattedExcinfo:
lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
return lines
- def get_exconly(
- self,
- excinfo: ExceptionInfo[BaseException],
- indent: int = 4,
- markall: bool = False,
- ) -> List[str]:
+ def get_exconly(
+ self,
+ excinfo: ExceptionInfo[BaseException],
+ indent: int = 4,
+ markall: bool = False,
+ ) -> List[str]:
lines = []
- indentstr = " " * indent
- # Get the real exception information out.
+ indentstr = " " * indent
+ # Get the real exception information out.
exlines = excinfo.exconly(tryshort=True).split("\n")
- failindent = self.fail_marker + indentstr[1:]
+ failindent = self.fail_marker + indentstr[1:]
for line in exlines:
lines.append(failindent + line)
if not markall:
- failindent = indentstr
+ failindent = indentstr
return lines
- def repr_locals(self, locals: Mapping[str, object]) -> Optional["ReprLocals"]:
+ def repr_locals(self, locals: Mapping[str, object]) -> Optional["ReprLocals"]:
if self.showlocals:
lines = []
keys = [loc for loc in locals if loc[0] != "@"]
@@ -767,32 +767,32 @@ class FormattedExcinfo:
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
if self.truncate_locals:
- str_repr = saferepr(value)
+ str_repr = saferepr(value)
else:
- str_repr = safeformat(value)
- # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)):
- lines.append(f"{name:<10} = {str_repr}")
+ str_repr = safeformat(value)
+ # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)):
+ lines.append(f"{name:<10} = {str_repr}")
# else:
# self._line("%-10s =\\" % (name,))
# # XXX
# pprint.pprint(value, stream=self.excinfowriter)
return ReprLocals(lines)
- return None
-
- def repr_traceback_entry(
- self,
- entry: TracebackEntry,
- excinfo: Optional[ExceptionInfo[BaseException]] = None,
- ) -> "ReprEntry":
- lines: List[str] = []
- style = entry._repr_style if entry._repr_style is not None else self.style
+ return None
+
+ def repr_traceback_entry(
+ self,
+ entry: TracebackEntry,
+ excinfo: Optional[ExceptionInfo[BaseException]] = None,
+ ) -> "ReprEntry":
+ lines: List[str] = []
+ style = entry._repr_style if entry._repr_style is not None else self.style
if style in ("short", "long"):
- source = self._getentrysource(entry)
- if source is None:
- source = Source("???")
- line_index = 0
- else:
- line_index = entry.lineno - entry.getfirstlinesource()
+ source = self._getentrysource(entry)
+ if source is None:
+ source = Source("???")
+ line_index = 0
+ else:
+ line_index = entry.lineno - entry.getfirstlinesource()
short = style == "short"
reprargs = self.repr_args(entry) if not short else None
s = self.get_source(source, line_index, excinfo, short=short)
@@ -802,17 +802,17 @@ class FormattedExcinfo:
else:
message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path)
- reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
- localsrepr = self.repr_locals(entry.locals)
- return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
- elif style == "value":
- if excinfo:
- lines.extend(str(excinfo.value).split("\n"))
- return ReprEntry(lines, None, None, None, style)
- else:
- if excinfo:
- lines.extend(self.get_exconly(excinfo, indent=4))
- return ReprEntry(lines, None, None, None, style)
+ reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
+ localsrepr = self.repr_locals(entry.locals)
+ return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
+ elif style == "value":
+ if excinfo:
+ lines.extend(str(excinfo.value).split("\n"))
+ return ReprEntry(lines, None, None, None, style)
+ else:
+ if excinfo:
+ lines.extend(self.get_exconly(excinfo, indent=4))
+ return ReprEntry(lines, None, None, None, style)
def _makepath(self, path):
if not self.abspath:
@@ -824,62 +824,62 @@ class FormattedExcinfo:
path = np
return path
- def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
+ def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
- if isinstance(excinfo.value, RecursionError):
+ if isinstance(excinfo.value, RecursionError):
traceback, extraline = self._truncate_recursive_traceback(traceback)
else:
extraline = None
last = traceback[-1]
entries = []
- if self.style == "value":
- reprentry = self.repr_traceback_entry(last, excinfo)
- entries.append(reprentry)
- return ReprTraceback(entries, None, style=self.style)
-
+ if self.style == "value":
+ reprentry = self.repr_traceback_entry(last, excinfo)
+ entries.append(reprentry)
+ return ReprTraceback(entries, None, style=self.style)
+
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
return ReprTraceback(entries, extraline, style=self.style)
- def _truncate_recursive_traceback(
- self, traceback: Traceback
- ) -> Tuple[Traceback, Optional[str]]:
- """Truncate the given recursive traceback trying to find the starting
- point of the recursion.
+ def _truncate_recursive_traceback(
+ self, traceback: Traceback
+ ) -> Tuple[Traceback, Optional[str]]:
+ """Truncate the given recursive traceback trying to find the starting
+ point of the recursion.
- The detection is done by going through each traceback entry and
- finding the point in which the locals of the frame are equal to the
- locals of a previous frame (see ``recursionindex()``).
+ The detection is done by going through each traceback entry and
+ finding the point in which the locals of the frame are equal to the
+ locals of a previous frame (see ``recursionindex()``).
- Handle the situation where the recursion process might raise an
- exception (for example comparing numpy arrays using equality raises a
- TypeError), in which case we do our best to warn the user of the
- error and show a limited traceback.
+ Handle the situation where the recursion process might raise an
+ exception (for example comparing numpy arrays using equality raises a
+ TypeError), in which case we do our best to warn the user of the
+ error and show a limited traceback.
"""
try:
recursionindex = traceback.recursionindex()
except Exception as e:
max_frames = 10
- extraline: Optional[str] = (
+ extraline: Optional[str] = (
"!!! Recursion error detected, but an error occurred locating the origin of recursion.\n"
" The following exception happened when comparing locals in the stack frame:\n"
" {exc_type}: {exc_msg}\n"
" Displaying first and last {max_frames} stack frames out of {total}."
).format(
exc_type=type(e).__name__,
- exc_msg=str(e),
+ exc_msg=str(e),
max_frames=max_frames,
total=len(traceback),
- )
- # Type ignored because adding two instaces of a List subtype
- # currently incorrectly has type List instead of the subtype.
- traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore
+ )
+ # Type ignored because adding two instaces of a List subtype
+ # currently incorrectly has type List instead of the subtype.
+ traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore
else:
if recursionindex is not None:
extraline = "!!! Recursion detected (same locals & position)"
@@ -889,136 +889,136 @@ class FormattedExcinfo:
return traceback, extraline
- def repr_excinfo(
- self, excinfo: ExceptionInfo[BaseException]
- ) -> "ExceptionChainRepr":
- repr_chain: List[
- Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]]
- ] = []
- e: Optional[BaseException] = excinfo.value
- excinfo_: Optional[ExceptionInfo[BaseException]] = excinfo
- descr = None
- seen: Set[int] = set()
- while e is not None and id(e) not in seen:
- seen.add(id(e))
- if excinfo_:
- reprtraceback = self.repr_traceback(excinfo_)
- reprcrash: Optional[ReprFileLocation] = (
- excinfo_._getreprcrash() if self.style != "value" else None
- )
- else:
- # Fallback to native repr if the exception doesn't have a traceback:
- # ExceptionInfo objects require a full traceback to work.
- reprtraceback = ReprTracebackNative(
- traceback.format_exception(type(e), e, None)
- )
- reprcrash = None
-
- repr_chain += [(reprtraceback, reprcrash, descr)]
- if e.__cause__ is not None and self.chain:
- e = e.__cause__
- excinfo_ = (
- ExceptionInfo((type(e), e, e.__traceback__))
- if e.__traceback__
- else None
- )
- descr = "The above exception was the direct cause of the following exception:"
- elif (
- e.__context__ is not None and not e.__suppress_context__ and self.chain
- ):
- e = e.__context__
- excinfo_ = (
- ExceptionInfo((type(e), e, e.__traceback__))
- if e.__traceback__
- else None
- )
- descr = "During handling of the above exception, another exception occurred:"
- else:
- e = None
- repr_chain.reverse()
- return ExceptionChainRepr(repr_chain)
-
-
-@attr.s(eq=False)
-class TerminalRepr:
- def __str__(self) -> str:
+ def repr_excinfo(
+ self, excinfo: ExceptionInfo[BaseException]
+ ) -> "ExceptionChainRepr":
+ repr_chain: List[
+ Tuple[ReprTraceback, Optional[ReprFileLocation], Optional[str]]
+ ] = []
+ e: Optional[BaseException] = excinfo.value
+ excinfo_: Optional[ExceptionInfo[BaseException]] = excinfo
+ descr = None
+ seen: Set[int] = set()
+ while e is not None and id(e) not in seen:
+ seen.add(id(e))
+ if excinfo_:
+ reprtraceback = self.repr_traceback(excinfo_)
+ reprcrash: Optional[ReprFileLocation] = (
+ excinfo_._getreprcrash() if self.style != "value" else None
+ )
+ else:
+ # Fallback to native repr if the exception doesn't have a traceback:
+ # ExceptionInfo objects require a full traceback to work.
+ reprtraceback = ReprTracebackNative(
+ traceback.format_exception(type(e), e, None)
+ )
+ reprcrash = None
+
+ repr_chain += [(reprtraceback, reprcrash, descr)]
+ if e.__cause__ is not None and self.chain:
+ e = e.__cause__
+ excinfo_ = (
+ ExceptionInfo((type(e), e, e.__traceback__))
+ if e.__traceback__
+ else None
+ )
+ descr = "The above exception was the direct cause of the following exception:"
+ elif (
+ e.__context__ is not None and not e.__suppress_context__ and self.chain
+ ):
+ e = e.__context__
+ excinfo_ = (
+ ExceptionInfo((type(e), e, e.__traceback__))
+ if e.__traceback__
+ else None
+ )
+ descr = "During handling of the above exception, another exception occurred:"
+ else:
+ e = None
+ repr_chain.reverse()
+ return ExceptionChainRepr(repr_chain)
+
+
+@attr.s(eq=False)
+class TerminalRepr:
+ def __str__(self) -> str:
# FYI this is called from pytest-xdist's serialization of exception
# information.
- io = StringIO()
- tw = TerminalWriter(file=io)
+ io = StringIO()
+ tw = TerminalWriter(file=io)
self.toterminal(tw)
return io.getvalue().strip()
- def __repr__(self) -> str:
- return "<{} instance at {:0x}>".format(self.__class__, id(self))
+ def __repr__(self) -> str:
+ return "<{} instance at {:0x}>".format(self.__class__, id(self))
+
+ def toterminal(self, tw: TerminalWriter) -> None:
+ raise NotImplementedError()
- def toterminal(self, tw: TerminalWriter) -> None:
- raise NotImplementedError()
-
-# This class is abstract -- only subclasses are instantiated.
-@attr.s(eq=False)
+# This class is abstract -- only subclasses are instantiated.
+@attr.s(eq=False)
class ExceptionRepr(TerminalRepr):
- # Provided by subclasses.
- reprcrash: Optional["ReprFileLocation"]
- reprtraceback: "ReprTraceback"
-
- def __attrs_post_init__(self) -> None:
- self.sections: List[Tuple[str, str, str]] = []
-
- def addsection(self, name: str, content: str, sep: str = "-") -> None:
+ # Provided by subclasses.
+ reprcrash: Optional["ReprFileLocation"]
+ reprtraceback: "ReprTraceback"
+
+ def __attrs_post_init__(self) -> None:
+ self.sections: List[Tuple[str, str, str]] = []
+
+ def addsection(self, name: str, content: str, sep: str = "-") -> None:
self.sections.append((name, content, sep))
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
for name, content, sep in self.sections:
tw.sep(sep, name)
tw.line(content)
-@attr.s(eq=False)
+@attr.s(eq=False)
class ExceptionChainRepr(ExceptionRepr):
- chain = attr.ib(
- type=Sequence[
- Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
- ]
- )
-
- def __attrs_post_init__(self) -> None:
- super().__attrs_post_init__()
+ chain = attr.ib(
+ type=Sequence[
+ Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
+ ]
+ )
+
+ def __attrs_post_init__(self) -> None:
+ super().__attrs_post_init__()
# reprcrash and reprtraceback of the outermost (the newest) exception
- # in the chain.
- self.reprtraceback = self.chain[-1][0]
- self.reprcrash = self.chain[-1][1]
+ # in the chain.
+ self.reprtraceback = self.chain[-1][0]
+ self.reprcrash = self.chain[-1][1]
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
for element in self.chain:
element[0].toterminal(tw)
if element[2] is not None:
tw.line("")
tw.line(element[2], yellow=True)
- super().toterminal(tw)
+ super().toterminal(tw)
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprExceptionInfo(ExceptionRepr):
- reprtraceback = attr.ib(type="ReprTraceback")
- reprcrash = attr.ib(type="ReprFileLocation")
+ reprtraceback = attr.ib(type="ReprTraceback")
+ reprcrash = attr.ib(type="ReprFileLocation")
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
self.reprtraceback.toterminal(tw)
- super().toterminal(tw)
+ super().toterminal(tw)
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprTraceback(TerminalRepr):
- reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]])
- extraline = attr.ib(type=Optional[str])
- style = attr.ib(type="_TracebackStyle")
-
+ reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]])
+ extraline = attr.ib(type=Optional[str])
+ style = attr.ib(type="_TracebackStyle")
+
entrysep = "_ "
- def toterminal(self, tw: TerminalWriter) -> None:
- # The entries might have different styles.
+ def toterminal(self, tw: TerminalWriter) -> None:
+ # The entries might have different styles.
for i, entry in enumerate(self.reprentries):
if entry.style == "long":
tw.line("")
@@ -1037,87 +1037,87 @@ class ReprTraceback(TerminalRepr):
class ReprTracebackNative(ReprTraceback):
- def __init__(self, tblines: Sequence[str]) -> None:
+ def __init__(self, tblines: Sequence[str]) -> None:
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprEntryNative(TerminalRepr):
- lines = attr.ib(type=Sequence[str])
- style: "_TracebackStyle" = "native"
+ lines = attr.ib(type=Sequence[str])
+ style: "_TracebackStyle" = "native"
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
tw.write("".join(self.lines))
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprEntry(TerminalRepr):
- lines = attr.ib(type=Sequence[str])
- reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"])
- reprlocals = attr.ib(type=Optional["ReprLocals"])
- reprfileloc = attr.ib(type=Optional["ReprFileLocation"])
- style = attr.ib(type="_TracebackStyle")
-
- def _write_entry_lines(self, tw: TerminalWriter) -> None:
- """Write the source code portions of a list of traceback entries with syntax highlighting.
-
- Usually entries are lines like these:
-
- " x = 1"
- "> assert x == 2"
- "E assert 1 == 2"
-
- This function takes care of rendering the "source" portions of it (the lines without
- the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
- character, as doing so might break line continuations.
- """
-
- if not self.lines:
- return
-
- # separate indents and source lines that are not failures: we want to
- # highlight the code but not the indentation, which may contain markers
- # such as "> assert 0"
- fail_marker = f"{FormattedExcinfo.fail_marker} "
- indent_size = len(fail_marker)
- indents: List[str] = []
- source_lines: List[str] = []
- failure_lines: List[str] = []
- for index, line in enumerate(self.lines):
- is_failure_line = line.startswith(fail_marker)
- if is_failure_line:
- # from this point on all lines are considered part of the failure
- failure_lines.extend(self.lines[index:])
- break
- else:
- if self.style == "value":
- source_lines.append(line)
- else:
- indents.append(line[:indent_size])
- source_lines.append(line[indent_size:])
-
- tw._write_source(source_lines, indents)
-
- # failure lines are always completely red and bold
- for line in failure_lines:
- tw.line(line, bold=True, red=True)
-
- def toterminal(self, tw: TerminalWriter) -> None:
+ lines = attr.ib(type=Sequence[str])
+ reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"])
+ reprlocals = attr.ib(type=Optional["ReprLocals"])
+ reprfileloc = attr.ib(type=Optional["ReprFileLocation"])
+ style = attr.ib(type="_TracebackStyle")
+
+ def _write_entry_lines(self, tw: TerminalWriter) -> None:
+ """Write the source code portions of a list of traceback entries with syntax highlighting.
+
+ Usually entries are lines like these:
+
+ " x = 1"
+ "> assert x == 2"
+ "E assert 1 == 2"
+
+ This function takes care of rendering the "source" portions of it (the lines without
+ the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
+ character, as doing so might break line continuations.
+ """
+
+ if not self.lines:
+ return
+
+ # separate indents and source lines that are not failures: we want to
+ # highlight the code but not the indentation, which may contain markers
+ # such as "> assert 0"
+ fail_marker = f"{FormattedExcinfo.fail_marker} "
+ indent_size = len(fail_marker)
+ indents: List[str] = []
+ source_lines: List[str] = []
+ failure_lines: List[str] = []
+ for index, line in enumerate(self.lines):
+ is_failure_line = line.startswith(fail_marker)
+ if is_failure_line:
+ # from this point on all lines are considered part of the failure
+ failure_lines.extend(self.lines[index:])
+ break
+ else:
+ if self.style == "value":
+ source_lines.append(line)
+ else:
+ indents.append(line[:indent_size])
+ source_lines.append(line[indent_size:])
+
+ tw._write_source(source_lines, indents)
+
+ # failure lines are always completely red and bold
+ for line in failure_lines:
+ tw.line(line, bold=True, red=True)
+
+ def toterminal(self, tw: TerminalWriter) -> None:
if self.style == "short":
- assert self.reprfileloc is not None
+ assert self.reprfileloc is not None
self.reprfileloc.toterminal(tw)
- self._write_entry_lines(tw)
- if self.reprlocals:
- self.reprlocals.toterminal(tw, indent=" " * 8)
+ self._write_entry_lines(tw)
+ if self.reprlocals:
+ self.reprlocals.toterminal(tw, indent=" " * 8)
return
-
+
if self.reprfuncargs:
self.reprfuncargs.toterminal(tw)
-
- self._write_entry_lines(tw)
-
+
+ self._write_entry_lines(tw)
+
if self.reprlocals:
tw.line("")
self.reprlocals.toterminal(tw)
@@ -1126,47 +1126,47 @@ class ReprEntry(TerminalRepr):
tw.line("")
self.reprfileloc.toterminal(tw)
- def __str__(self) -> str:
- return "{}\n{}\n{}".format(
- "\n".join(self.lines), self.reprlocals, self.reprfileloc
- )
+ def __str__(self) -> str:
+ return "{}\n{}\n{}".format(
+ "\n".join(self.lines), self.reprlocals, self.reprfileloc
+ )
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprFileLocation(TerminalRepr):
- path = attr.ib(type=str, converter=str)
- lineno = attr.ib(type=int)
- message = attr.ib(type=str)
+ path = attr.ib(type=str, converter=str)
+ lineno = attr.ib(type=int)
+ message = attr.ib(type=str)
- def toterminal(self, tw: TerminalWriter) -> None:
- # Filename and lineno output for each entry, using an output format
- # that most editors understand.
+ def toterminal(self, tw: TerminalWriter) -> None:
+ # Filename and lineno output for each entry, using an output format
+ # that most editors understand.
msg = self.message
i = msg.find("\n")
if i != -1:
msg = msg[:i]
tw.write(self.path, bold=True, red=True)
- tw.line(f":{self.lineno}: {msg}")
+ tw.line(f":{self.lineno}: {msg}")
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprLocals(TerminalRepr):
- lines = attr.ib(type=Sequence[str])
+ lines = attr.ib(type=Sequence[str])
- def toterminal(self, tw: TerminalWriter, indent="") -> None:
+ def toterminal(self, tw: TerminalWriter, indent="") -> None:
for line in self.lines:
- tw.line(indent + line)
+ tw.line(indent + line)
-@attr.s(eq=False)
+@attr.s(eq=False)
class ReprFuncArgs(TerminalRepr):
- args = attr.ib(type=Sequence[Tuple[str, object]])
+ args = attr.ib(type=Sequence[Tuple[str, object]])
- def toterminal(self, tw: TerminalWriter) -> None:
+ def toterminal(self, tw: TerminalWriter) -> None:
if self.args:
linesofar = ""
for name, value in self.args:
- ns = f"{name} = {value}"
+ ns = f"{name} = {value}"
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
@@ -1181,79 +1181,79 @@ class ReprFuncArgs(TerminalRepr):
tw.line("")
-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.
- """
- # xxx let decorators etc specify a sane ordering
- # NOTE: this used to be done in _pytest.compat.getfslineno, initially added
- # 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 # type: ignore[attr-defined]
-
+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.
+ """
+ # xxx let decorators etc specify a sane ordering
+ # NOTE: this used to be done in _pytest.compat.getfslineno, initially added
+ # 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 # type: ignore[attr-defined]
+
try:
- code = Code.from_function(obj)
- except TypeError:
- try:
- fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type]
- except TypeError:
- return "", -1
-
- fspath = fn and py.path.local(fn) or ""
- lineno = -1
- if fspath:
- try:
- _, lineno = findsource(obj)
- except OSError:
- pass
- return fspath, lineno
-
- return code.path, code.firstlineno
-
-
-# Relative paths that we use to filter traceback entries from appearing to the user;
-# see filter_traceback.
+ code = Code.from_function(obj)
+ except TypeError:
+ try:
+ fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type]
+ except TypeError:
+ return "", -1
+
+ fspath = fn and py.path.local(fn) or ""
+ lineno = -1
+ if fspath:
+ try:
+ _, lineno = findsource(obj)
+ except OSError:
+ pass
+ return fspath, lineno
+
+ return code.path, code.firstlineno
+
+
+# Relative paths that we use to filter traceback entries from appearing to the user;
+# see filter_traceback.
# note: if we need to add more paths than what we have now we should probably use a list
-# for better maintenance.
+# for better maintenance.
-_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc"))
+_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc"))
# pluggy is either a package or a single module depending on the version
-if _PLUGGY_DIR.name == "__init__.py":
- _PLUGGY_DIR = _PLUGGY_DIR.parent
-_PYTEST_DIR = Path(_pytest.__file__).parent
-_PY_DIR = Path(py.__file__).parent
+if _PLUGGY_DIR.name == "__init__.py":
+ _PLUGGY_DIR = _PLUGGY_DIR.parent
+_PYTEST_DIR = Path(_pytest.__file__).parent
+_PY_DIR = Path(py.__file__).parent
+
+def filter_traceback(entry: TracebackEntry) -> bool:
+ """Return True if a TracebackEntry instance should be included in tracebacks.
+
+ We hide traceback entries of:
-def filter_traceback(entry: TracebackEntry) -> bool:
- """Return True if a TracebackEntry instance should be included in tracebacks.
-
- We hide traceback entries of:
-
* dynamically generated code (no code to show up for it);
* internal traceback from pytest or its internal libraries, py and pluggy.
"""
# entry.path might sometimes return a str object when the entry
- # points to dynamically generated code.
- # See https://bitbucket.org/pytest-dev/py/issues/71.
+ # points to dynamically generated code.
+ # See https://bitbucket.org/pytest-dev/py/issues/71.
raw_filename = entry.frame.code.raw.co_filename
is_generated = "<" in raw_filename and ">" in raw_filename
if is_generated:
return False
-
+
# entry.path might point to a non-existing file, in which case it will
- # also return a str object. See #1133.
- p = Path(entry.path)
-
- parents = p.parents
- if _PLUGGY_DIR in parents:
- return False
- if _PYTEST_DIR in parents:
- return False
- if _PY_DIR in parents:
- return False
-
- return True
+ # also return a str object. See #1133.
+ p = Path(entry.path)
+
+ parents = p.parents
+ if _PLUGGY_DIR in parents:
+ return False
+ if _PYTEST_DIR in parents:
+ return False
+ if _PY_DIR in parents:
+ return False
+
+ return True
diff --git a/contrib/python/pytest/py3/_pytest/_code/source.py b/contrib/python/pytest/py3/_pytest/_code/source.py
index 84bac0a8a8..6f54057c0a 100644
--- a/contrib/python/pytest/py3/_pytest/_code/source.py
+++ b/contrib/python/pytest/py3/_pytest/_code/source.py
@@ -2,58 +2,58 @@ import ast
import inspect
import textwrap
import tokenize
-import types
+import types
import warnings
from bisect import bisect_right
-from typing import Iterable
-from typing import Iterator
-from typing import List
-from typing import Optional
-from typing import overload
-from typing import Tuple
-from typing import Union
+from typing import Iterable
+from typing import Iterator
+from typing import List
+from typing import Optional
+from typing import overload
+from typing import Tuple
+from typing import Union
-class Source:
- """An immutable object holding a source code fragment.
+class Source:
+ """An immutable object holding a source code fragment.
- When using Source(...), the source lines are deindented.
+ When using Source(...), the source lines are deindented.
"""
- def __init__(self, obj: object = None) -> None:
- if not obj:
- self.lines: List[str] = []
- elif isinstance(obj, Source):
- self.lines = obj.lines
- elif isinstance(obj, (tuple, list)):
- self.lines = deindent(x.rstrip("\n") for x in obj)
- elif isinstance(obj, str):
- self.lines = deindent(obj.split("\n"))
- else:
- try:
- rawcode = getrawcode(obj)
- src = inspect.getsource(rawcode)
- except TypeError:
- src = inspect.getsource(obj) # type: ignore[arg-type]
- self.lines = deindent(src.split("\n"))
-
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, Source):
- return NotImplemented
- return self.lines == other.lines
-
- # Ignore type because of https://github.com/python/mypy/issues/4266.
- __hash__ = None # type: ignore
-
- @overload
- def __getitem__(self, key: int) -> str:
- ...
-
- @overload
- def __getitem__(self, key: slice) -> "Source":
- ...
-
- def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]:
+ def __init__(self, obj: object = None) -> None:
+ if not obj:
+ self.lines: List[str] = []
+ elif isinstance(obj, Source):
+ self.lines = obj.lines
+ elif isinstance(obj, (tuple, list)):
+ self.lines = deindent(x.rstrip("\n") for x in obj)
+ elif isinstance(obj, str):
+ self.lines = deindent(obj.split("\n"))
+ else:
+ try:
+ rawcode = getrawcode(obj)
+ src = inspect.getsource(rawcode)
+ except TypeError:
+ src = inspect.getsource(obj) # type: ignore[arg-type]
+ self.lines = deindent(src.split("\n"))
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, Source):
+ return NotImplemented
+ return self.lines == other.lines
+
+ # Ignore type because of https://github.com/python/mypy/issues/4266.
+ __hash__ = None # type: ignore
+
+ @overload
+ def __getitem__(self, key: int) -> str:
+ ...
+
+ @overload
+ def __getitem__(self, key: slice) -> "Source":
+ ...
+
+ def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]:
if isinstance(key, int):
return self.lines[key]
else:
@@ -63,14 +63,14 @@ class Source:
newsource.lines = self.lines[key.start : key.stop]
return newsource
- def __iter__(self) -> Iterator[str]:
- return iter(self.lines)
-
- def __len__(self) -> int:
+ def __iter__(self) -> Iterator[str]:
+ return iter(self.lines)
+
+ def __len__(self) -> int:
return len(self.lines)
- def strip(self) -> "Source":
- """Return new Source object with trailing and leading blank lines removed."""
+ def strip(self) -> "Source":
+ """Return new Source object with trailing and leading blank lines removed."""
start, end = 0, len(self)
while start < end and not self.lines[start].strip():
start += 1
@@ -80,80 +80,80 @@ class Source:
source.lines[:] = self.lines[start:end]
return source
- def indent(self, indent: str = " " * 4) -> "Source":
- """Return a copy of the source object with all lines indented by the
- given indent-string."""
+ def indent(self, indent: str = " " * 4) -> "Source":
+ """Return a copy of the source object with all lines indented by the
+ given indent-string."""
newsource = Source()
newsource.lines = [(indent + line) for line in self.lines]
return newsource
- def getstatement(self, lineno: int) -> "Source":
- """Return Source statement which contains the given linenumber
- (counted from 0)."""
+ def getstatement(self, lineno: int) -> "Source":
+ """Return Source statement which contains the given linenumber
+ (counted from 0)."""
start, end = self.getstatementrange(lineno)
return self[start:end]
- def getstatementrange(self, lineno: int) -> Tuple[int, int]:
- """Return (start, end) tuple which spans the minimal statement region
- which containing the given lineno."""
+ def getstatementrange(self, lineno: int) -> Tuple[int, int]:
+ """Return (start, end) tuple which spans the minimal statement region
+ which containing the given lineno."""
if not (0 <= lineno < len(self)):
raise IndexError("lineno out of range")
ast, start, end = getstatementrange_ast(lineno, self)
return start, end
- def deindent(self) -> "Source":
- """Return a new Source object deindented."""
+ def deindent(self) -> "Source":
+ """Return a new Source object deindented."""
newsource = Source()
newsource.lines[:] = deindent(self.lines)
return newsource
- def __str__(self) -> str:
+ def __str__(self) -> str:
return "\n".join(self.lines)
-
+
#
# helper functions
#
-def findsource(obj) -> Tuple[Optional[Source], int]:
+def findsource(obj) -> Tuple[Optional[Source], int]:
try:
sourcelines, lineno = inspect.findsource(obj)
- except Exception:
+ except Exception:
return None, -1
source = Source()
source.lines = [line.rstrip() for line in sourcelines]
return source, lineno
-def getrawcode(obj: object, trycall: bool = True) -> types.CodeType:
- """Return code object for given function."""
+def getrawcode(obj: object, trycall: bool = True) -> types.CodeType:
+ """Return code object for given function."""
try:
- return obj.__code__ # type: ignore[attr-defined,no-any-return]
- except AttributeError:
- pass
- if trycall:
- call = getattr(obj, "__call__", None)
- if call and not isinstance(obj, type):
- return getrawcode(call, trycall=False)
- raise TypeError(f"could not get code object for {obj!r}")
+ return obj.__code__ # type: ignore[attr-defined,no-any-return]
+ except AttributeError:
+ pass
+ if trycall:
+ call = getattr(obj, "__call__", None)
+ if call and not isinstance(obj, type):
+ return getrawcode(call, trycall=False)
+ raise TypeError(f"could not get code object for {obj!r}")
-def deindent(lines: Iterable[str]) -> List[str]:
+def deindent(lines: Iterable[str]) -> List[str]:
return textwrap.dedent("\n".join(lines)).splitlines()
-def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]:
- # Flatten all statements and except handlers into one lineno-list.
- # AST's line numbers start indexing at 1.
- values: List[int] = []
+def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]:
+ # Flatten all statements and except handlers into one lineno-list.
+ # AST's line numbers start indexing at 1.
+ values: List[int] = []
for x in ast.walk(node):
if isinstance(x, (ast.stmt, ast.ExceptHandler)):
values.append(x.lineno - 1)
for name in ("finalbody", "orelse"):
- val: Optional[List[ast.stmt]] = getattr(x, name, None)
+ val: Optional[List[ast.stmt]] = getattr(x, name, None)
if val:
- # Treat the finally/orelse part as its own statement.
+ # Treat the finally/orelse part as its own statement.
values.append(val[0].lineno - 1 - 1)
values.sort()
insert_index = bisect_right(values, lineno)
@@ -165,22 +165,22 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i
return start, end
-def getstatementrange_ast(
- lineno: int,
- source: Source,
- assertion: bool = False,
- astnode: Optional[ast.AST] = None,
-) -> Tuple[ast.AST, int, int]:
+def getstatementrange_ast(
+ lineno: int,
+ source: Source,
+ assertion: bool = False,
+ astnode: Optional[ast.AST] = None,
+) -> Tuple[ast.AST, int, int]:
if astnode is None:
content = str(source)
# See #4260:
- # Don't produce duplicate warnings when compiling source to find AST.
+ # Don't produce duplicate warnings when compiling source to find AST.
with warnings.catch_warnings():
warnings.simplefilter("ignore")
- astnode = ast.parse(content, "source", "exec")
+ astnode = ast.parse(content, "source", "exec")
start, end = get_statement_startend2(lineno, astnode)
- # We need to correct the end:
+ # We need to correct the end:
# - ast-parsing strips comments
# - there might be empty lines
# - we might have lesser indented code blocks at the end
@@ -188,10 +188,10 @@ def getstatementrange_ast(
end = len(source.lines)
if end > start + 1:
- # Make sure we don't span differently indented code blocks
- # by using the BlockFinder helper used which inspect.getsource() uses itself.
+ # Make sure we don't span differently indented code blocks
+ # by using the BlockFinder helper used which inspect.getsource() uses itself.
block_finder = inspect.BlockFinder()
- # If we start with an indented line, put blockfinder to "started" mode.
+ # If we start with an indented line, put blockfinder to "started" mode.
block_finder.started = source.lines[start][0].isspace()
it = ((x + "\n") for x in source.lines[start:end])
try:
@@ -202,7 +202,7 @@ def getstatementrange_ast(
except Exception:
pass
- # The end might still point to a comment or empty line, correct it.
+ # The end might still point to a comment or empty line, correct it.
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line: