aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/_code
diff options
context:
space:
mode:
authordeshevoy <deshevoy@yandex-team.ru>2022-02-10 16:46:57 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:46:57 +0300
commit28148f76dbfcc644d96427d41c92f36cbf2fdc6e (patch)
treeb83306b6e37edeea782e9eed673d89286c4fef35 /contrib/python/pytest/py3/_pytest/_code
parente988f30484abe5fdeedcc7a5d3c226c01a21800c (diff)
downloadydb-28148f76dbfcc644d96427d41c92f36cbf2fdc6e.tar.gz
Restoring authorship annotation for <deshevoy@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/code.py1082
-rw-r--r--contrib/python/pytest/py3/_pytest/_code/source.py240
2 files changed, 661 insertions, 661 deletions
diff --git a/contrib/python/pytest/py3/_pytest/_code/code.py b/contrib/python/pytest/py3/_pytest/_code/code.py
index 576a491d70..423069330a 100644
--- a/contrib/python/pytest/py3/_pytest/_code/code.py
+++ b/contrib/python/pytest/py3/_pytest/_code/code.py
@@ -1,9 +1,9 @@
-import inspect
-import re
-import sys
-import traceback
-from inspect import CO_VARARGS
-from inspect import CO_VARKEYWORDS
+import inspect
+import re
+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
@@ -27,13 +27,13 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
-from weakref import ref
-
-import attr
-import pluggy
-import py
-
-import _pytest
+from weakref import ref
+
+import attr
+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
@@ -43,19 +43,19 @@ 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
@@ -63,17 +63,17 @@ class Code:
def from_function(cls, obj: object) -> "Code":
return cls(getrawcode(obj))
- def __eq__(self, other):
- return self.raw == other.raw
-
+ def __eq__(self, other):
+ return self.raw == other.raw
+
# 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
+
+ @property
def name(self) -> str:
return self.raw.co_name
@@ -83,53 +83,53 @@ class Code:
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.")
+ 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
- except OSError:
- # XXX maybe try harder like the weird logic
- # in the standard lib [linecache.updatecache] does?
+ except OSError:
+ # XXX maybe try harder like the weird logic
+ # in the standard lib [linecache.updatecache] does?
return self.raw.co_filename
-
- @property
+
+ @property
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
-
+ return full
+
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 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.
-
+
If 'var' is set True also return the names of the variable and
keyword arguments when present.
- """
+ """
# Handy shortcut for getting args.
- raw = self.raw
- argcount = raw.co_argcount
- if var:
- argcount += raw.co_flags & CO_VARARGS
- argcount += raw.co_flags & CO_VARKEYWORDS
- return raw.co_varnames[:argcount]
-
-
+ raw = self.raw
+ argcount = raw.co_argcount
+ if var:
+ argcount += raw.co_flags & CO_VARARGS
+ argcount += raw.co_flags & CO_VARKEYWORDS
+ return raw.co_varnames[:argcount]
+
+
class Frame:
- """Wrapper around a Python frame holding f_locals and f_globals
- in which expressions can be evaluated."""
-
+ """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:
- self.raw = frame
-
- @property
+ self.raw = frame
+
+ @property
def lineno(self) -> int:
return self.raw.f_lineno - 1
@@ -148,128 +148,128 @@ class Frame:
@property
def statement(self) -> "Source":
"""Statement this frame is at."""
- if self.code.fullsource is None:
+ if self.code.fullsource is None:
return Source("")
- return self.code.fullsource.getstatement(self.lineno)
-
- def eval(self, code, **vars):
+ return self.code.fullsource.getstatement(self.lineno)
+
+ def eval(self, code, **vars):
"""Evaluate 'code' in the frame.
-
+
'vars' are optional additional local variables.
-
+
Returns the result of the evaluation.
- """
- f_locals = self.f_locals.copy()
- f_locals.update(vars)
- return eval(code, self.f_globals, f_locals)
-
+ """
+ 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 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.
- """
- retval = []
- for arg in self.code.getargs(var):
- try:
- retval.append((arg, self.f_locals[arg]))
- except KeyError:
- pass # this can occur when using Psyco
- return retval
-
-
+ """
+ retval = []
+ for arg in self.code.getargs(var):
+ try:
+ retval.append((arg, self.f_locals[arg]))
+ except KeyError:
+ pass # this can occur when using Psyco
+ return retval
+
+
class TracebackEntry:
"""A single entry in a Traceback."""
-
+
__slots__ = ("_rawentry", "_excinfo", "_repr_style")
-
+
def __init__(
self,
rawentry: TracebackType,
excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None,
) -> None:
self._rawentry = rawentry
- self._excinfo = excinfo
+ self._excinfo = excinfo
self._repr_style: Optional['Literal["short", "long"]'] = None
-
+
@property
def lineno(self) -> int:
return self._rawentry.tb_lineno - 1
def set_repr_style(self, mode: "Literal['short', 'long']") -> None:
- assert mode in ("short", "long")
- self._repr_style = mode
-
- @property
+ assert mode in ("short", "long")
+ self._repr_style = mode
+
+ @property
def frame(self) -> Frame:
return Frame(self._rawentry.tb_frame)
-
- @property
+
+ @property
def relline(self) -> int:
- return self.lineno - self.frame.code.firstlineno
-
+ return self.lineno - self.frame.code.firstlineno
+
def __repr__(self) -> str:
- return "<TracebackEntry %s:%d>" % (self.frame.code.path, self.lineno + 1)
-
- @property
+ return "<TracebackEntry %s:%d>" % (self.frame.code.path, self.lineno + 1)
+
+ @property
def statement(self) -> "Source":
"""_pytest._code.Source object for the current statement."""
- source = self.frame.code.fullsource
+ source = self.frame.code.fullsource
assert source is not None
- return source.getstatement(self.lineno)
-
- @property
+ return source.getstatement(self.lineno)
+
+ @property
def path(self) -> Union[py.path.local, str]:
"""Path to the source code."""
- return self.frame.code.path
-
+ return self.frame.code.path
+
@property
def locals(self) -> Dict[str, Any]:
"""Locals of underlying frame."""
- return self.frame.f_locals
-
+ 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."""
- # we use the passed in astcache to not reparse asttrees
- # within exception info printing
- source = self.frame.code.fullsource
- if source is None:
- return None
- key = astnode = None
- if astcache is not None:
- key = self.frame.code.path
- if key is not None:
- astnode = astcache.get(key, None)
- start = self.getfirstlinesource()
- try:
- astnode, _, end = getstatementrange_ast(
- self.lineno, source, astnode=astnode
- )
- except SyntaxError:
- end = self.lineno + 1
- else:
- if key is not None:
- astcache[key] = astnode
- return source[start:end]
-
- source = property(getsource)
-
+ # we use the passed in astcache to not reparse asttrees
+ # within exception info printing
+ source = self.frame.code.fullsource
+ if source is None:
+ return None
+ key = astnode = None
+ if astcache is not None:
+ key = self.frame.code.path
+ if key is not None:
+ astnode = astcache.get(key, None)
+ start = self.getfirstlinesource()
+ try:
+ astnode, _, end = getstatementrange_ast(
+ self.lineno, source, astnode=astnode
+ )
+ except SyntaxError:
+ end = self.lineno + 1
+ else:
+ if key is not None:
+ astcache[key] = astnode
+ return source[start:end]
+
+ source = property(getsource)
+
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.
-
+
Mostly for internal use.
- """
+ """
tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = (
False
)
@@ -285,17 +285,17 @@ class TracebackEntry:
else:
break
if tbh and callable(tbh):
- return tbh(None if self._excinfo is None else self._excinfo())
+ return tbh(None if self._excinfo is None else self._excinfo())
return tbh
-
+
def __str__(self) -> str:
- name = self.frame.code.name
- try:
- line = str(self.statement).lstrip()
- except KeyboardInterrupt:
- raise
+ name = self.frame.code.name
+ try:
+ line = str(self.statement).lstrip()
+ except KeyboardInterrupt:
+ raise
except BaseException:
- line = "???"
+ 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.
@@ -305,35 +305,35 @@ class TracebackEntry:
name,
line,
)
-
+
@property
def name(self) -> str:
"""co_name of underlying code."""
- return self.frame.code.raw.co_name
-
-
+ return self.frame.code.raw.co_name
+
+
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."""
- self._excinfo = excinfo
+ self._excinfo = excinfo
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
-
+
super().__init__(f(tb))
- else:
+ else:
super().__init__(tb)
-
+
def cut(
self,
path=None,
@@ -342,34 +342,34 @@ class Traceback(List[TracebackEntry]):
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
- codepath = code.path
- if (
- (path is None or codepath == path)
- and (
- excludepath is None
+ """
+ for x in self:
+ code = x.frame.code
+ codepath = code.path
+ if (
+ (path is None or codepath == path)
+ and (
+ excludepath is None
or not isinstance(codepath, py.path.local)
- or not codepath.relto(excludepath)
- )
- and (lineno is None or x.lineno == lineno)
- and (firstlineno is None or x.frame.code.firstlineno == firstlineno)
- ):
- return Traceback(x._rawentry, self._excinfo)
- return self
-
+ or not codepath.relto(excludepath)
+ )
+ and (lineno is None or x.lineno == lineno)
+ and (firstlineno is None or x.frame.code.firstlineno == firstlineno)
+ ):
+ return Traceback(x._rawentry, self._excinfo)
+ return self
+
@overload
def __getitem__(self, key: int) -> TracebackEntry:
...
-
+
@overload
def __getitem__(self, key: slice) -> "Traceback":
...
@@ -384,55 +384,55 @@ class Traceback(List[TracebackEntry]):
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)
-
+ """
+ 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."""
- for i in range(-1, -len(self) - 1, -1):
- entry = self[i]
- if not entry.ishidden():
- return entry
- return self[-1]
-
+ 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]]] = {}
- 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
- # which generates code objects that have hash/value equality
- # XXX needs a test
- key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
- # print "checking for recursion at", key
- values = cache.setdefault(key, [])
- if values:
- f = entry.frame
- loc = f.f_locals
- for otherloc in values:
+ 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
+ # which generates code objects that have hash/value equality
+ # XXX needs a test
+ key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
+ # print "checking for recursion at", key
+ values = cache.setdefault(key, [])
+ if values:
+ f = entry.frame
+ loc = f.f_locals
+ for otherloc in values:
if f.eval(
co_equal,
__recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc,
- ):
- return i
- values.append(entry.frame.f_locals)
- return None
-
-
-co_equal = compile(
- "__recursioncache_locals_1 == __recursioncache_locals_2", "?", "eval"
-)
-
-
+ ):
+ return i
+ values.append(entry.frame.f_locals)
+ return None
+
+
+co_equal = compile(
+ "__recursioncache_locals_1 == __recursioncache_locals_2", "?", "eval"
+)
+
+
_E = TypeVar("_E", bound=BaseException, covariant=True)
@@ -440,13 +440,13 @@ _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."""
-
+
_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,
@@ -454,7 +454,7 @@ class ExceptionInfo(Generic[_E]):
exprinfo: Optional[str] = None,
) -> "ExceptionInfo[_E]":
"""Return an ExceptionInfo for an existing exc_info tuple.
-
+
.. warning::
Experimental API
@@ -555,23 +555,23 @@ class ExceptionInfo(Generic[_E]):
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)
- text = text.rstrip()
- if tryshort:
- if text.startswith(self._striptext):
- text = text[len(self._striptext) :]
- return text
-
+ """
+ lines = format_exception_only(self.type, self.value)
+ text = "".join(lines)
+ text = text.rstrip()
+ if tryshort:
+ if text.startswith(self._striptext):
+ text = text[len(self._striptext) :]
+ return text
+
def errisinstance(
self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]]
) -> bool:
@@ -579,16 +579,16 @@ class ExceptionInfo(Generic[_E]):
Consider using ``isinstance(excinfo.value, exc)`` instead.
"""
- return isinstance(self.value, exc)
-
+ return isinstance(self.value, exc)
+
def _getreprcrash(self) -> "ReprFileLocation":
- exconly = self.exconly(tryshort=True)
- entry = self.traceback.getcrashentry()
- path, lineno = entry.frame.code.raw.co_filename, entry.lineno
- return ReprFileLocation(path, lineno + 1, exconly)
-
- def getrepr(
- self,
+ exconly = self.exconly(tryshort=True)
+ entry = self.traceback.getcrashentry()
+ path, lineno = entry.frame.code.raw.co_filename, entry.lineno
+ return ReprFileLocation(path, lineno + 1, exconly)
+
+ def getrepr(
+ self,
showlocals: bool = False,
style: "_TracebackStyle" = "long",
abspath: bool = False,
@@ -598,78 +598,78 @@ class ExceptionInfo(Generic[_E]):
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 bool showlocals:
+ Show locals per traceback entry.
+ Ignored if ``style=="native"``.
+
:param str style:
long|short|no|native|value traceback style.
-
- :param bool abspath:
- If paths should be changed to absolute or left unchanged.
-
- :param bool tbfilter:
- Hide entries that contain a local variable ``__tracebackhide__==True``.
- Ignored if ``style=="native"``.
-
- :param bool funcargs:
- Show fixtures ("funcargs" for legacy purposes) per traceback entry.
-
- :param bool truncate_locals:
- With ``showlocals==True``, make sure locals can be safely represented as strings.
-
+
+ :param bool abspath:
+ If paths should be changed to absolute or left unchanged.
+
+ :param bool tbfilter:
+ Hide entries that contain a local variable ``__tracebackhide__==True``.
+ Ignored if ``style=="native"``.
+
+ :param bool funcargs:
+ Show fixtures ("funcargs" for legacy purposes) per traceback entry.
+
+ :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.
-
- .. versionchanged:: 3.9
-
- Added the ``chain`` parameter.
- """
- if style == "native":
- return ReprExceptionInfo(
- ReprTracebackNative(
- traceback.format_exception(
- self.type, self.value, self.traceback[0]._rawentry
- )
- ),
- self._getreprcrash(),
- )
-
- fmt = FormattedExcinfo(
- showlocals=showlocals,
- style=style,
- abspath=abspath,
- tbfilter=tbfilter,
- funcargs=funcargs,
- truncate_locals=truncate_locals,
- chain=chain,
- )
- return fmt.repr_excinfo(self)
-
+
+ .. versionchanged:: 3.9
+
+ Added the ``chain`` parameter.
+ """
+ if style == "native":
+ return ReprExceptionInfo(
+ ReprTracebackNative(
+ traceback.format_exception(
+ self.type, self.value, self.traceback[0]._rawentry
+ )
+ ),
+ self._getreprcrash(),
+ )
+
+ fmt = FormattedExcinfo(
+ showlocals=showlocals,
+ style=style,
+ abspath=abspath,
+ tbfilter=tbfilter,
+ funcargs=funcargs,
+ truncate_locals=truncate_locals,
+ chain=chain,
+ )
+ 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.
- """
- __tracebackhide__ = True
+ """
+ __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()".
- return True
-
-
-@attr.s
+ return True
+
+
+@attr.s
class FormattedExcinfo:
"""Presenting information about failing Functions and Generators."""
-
- # for traceback entries
- flow_marker = ">"
- fail_marker = "E"
-
+
+ # 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)
@@ -677,37 +677,37 @@ class FormattedExcinfo:
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)
-
+ astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
+
def _getindent(self, source: "Source") -> int:
# Figure out indent for the given source.
- try:
- s = str(source.getstatement(len(source) - 1))
- except KeyboardInterrupt:
- raise
+ try:
+ s = str(source.getstatement(len(source) - 1))
+ except KeyboardInterrupt:
+ raise
except BaseException:
- try:
- s = str(source[-1])
- except KeyboardInterrupt:
- raise
+ try:
+ s = str(source[-1])
+ except KeyboardInterrupt:
+ raise
except BaseException:
- return 0
- return 4 + (len(s) - len(s.lstrip()))
-
+ return 0
+ return 4 + (len(s) - len(s.lstrip()))
+
def _getentrysource(self, entry: TracebackEntry) -> Optional["Source"]:
- source = entry.getsource(self.astcache)
- if source is not None:
- source = source.deindent()
- return source
-
+ source = entry.getsource(self.astcache)
+ if source is not None:
+ source = source.deindent()
+ return source
+
def repr_args(self, entry: TracebackEntry) -> Optional["ReprFuncArgs"]:
- if self.funcargs:
- args = []
- for argname, argvalue in entry.frame.getargs(var=True):
+ if self.funcargs:
+ args = []
+ for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, saferepr(argvalue)))
- return ReprFuncArgs(args)
+ return ReprFuncArgs(args)
return None
-
+
def get_source(
self,
source: Optional["Source"],
@@ -716,69 +716,69 @@ class FormattedExcinfo:
short: bool = False,
) -> List[str]:
"""Return formatted and marked up source lines."""
- lines = []
- if source is None or line_index >= len(source.lines):
+ lines = []
+ if source is None or line_index >= len(source.lines):
source = Source("???")
- line_index = 0
- if line_index < 0:
- line_index += len(source)
- space_prefix = " "
- if short:
- lines.append(space_prefix + source.lines[line_index].strip())
- else:
- for line in source.lines[:line_index]:
- lines.append(space_prefix + line)
- lines.append(self.flow_marker + " " + source.lines[line_index])
- for line in source.lines[line_index + 1 :]:
- lines.append(space_prefix + line)
- if excinfo is not None:
- indent = 4 if short else self._getindent(source)
- lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
- return lines
-
+ line_index = 0
+ if line_index < 0:
+ line_index += len(source)
+ space_prefix = " "
+ if short:
+ lines.append(space_prefix + source.lines[line_index].strip())
+ else:
+ for line in source.lines[:line_index]:
+ lines.append(space_prefix + line)
+ lines.append(self.flow_marker + " " + source.lines[line_index])
+ for line in source.lines[line_index + 1 :]:
+ lines.append(space_prefix + line)
+ if excinfo is not None:
+ indent = 4 if short else self._getindent(source)
+ 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]:
- lines = []
+ lines = []
indentstr = " " * indent
# Get the real exception information out.
- exlines = excinfo.exconly(tryshort=True).split("\n")
+ exlines = excinfo.exconly(tryshort=True).split("\n")
failindent = self.fail_marker + indentstr[1:]
- for line in exlines:
- lines.append(failindent + line)
- if not markall:
+ for line in exlines:
+ lines.append(failindent + line)
+ if not markall:
failindent = indentstr
- return lines
-
+ return lines
+
def repr_locals(self, locals: Mapping[str, object]) -> Optional["ReprLocals"]:
- if self.showlocals:
- lines = []
- keys = [loc for loc in locals if loc[0] != "@"]
- keys.sort()
- for name in keys:
- value = locals[name]
- if name == "__builtins__":
- lines.append("__builtins__ = <builtins>")
- else:
- # This formatting could all be handled by the
- # _repr() function, which is only reprlib.Repr in
- # disguise, so is very configurable.
- if self.truncate_locals:
+ if self.showlocals:
+ lines = []
+ keys = [loc for loc in locals if loc[0] != "@"]
+ keys.sort()
+ for name in keys:
+ value = locals[name]
+ if name == "__builtins__":
+ lines.append("__builtins__ = <builtins>")
+ else:
+ # This formatting could all be handled by the
+ # _repr() function, which is only reprlib.Repr in
+ # disguise, so is very configurable.
+ if self.truncate_locals:
str_repr = saferepr(value)
- else:
+ else:
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)
+ # else:
+ # self._line("%-10s =\\" % (name,))
+ # # XXX
+ # pprint.pprint(value, stream=self.excinfowriter)
+ return ReprLocals(lines)
return None
-
+
def repr_traceback_entry(
self,
entry: TracebackEntry,
@@ -786,22 +786,22 @@ class FormattedExcinfo:
) -> "ReprEntry":
lines: List[str] = []
style = entry._repr_style if entry._repr_style is not None else self.style
- if style in ("short", "long"):
+ 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()
- short = style == "short"
- reprargs = self.repr_args(entry) if not short else None
- s = self.get_source(source, line_index, excinfo, short=short)
- lines.extend(s)
- if short:
- message = "in %s" % (entry.name)
- else:
- message = excinfo and excinfo.typename or ""
- path = self._makepath(entry.path)
+ short = style == "short"
+ reprargs = self.repr_args(entry) if not short else None
+ s = self.get_source(source, line_index, excinfo, short=short)
+ lines.extend(s)
+ if short:
+ message = "in %s" % (entry.name)
+ 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)
@@ -813,82 +813,82 @@ class FormattedExcinfo:
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:
- try:
- np = py.path.local().bestrelpath(path)
- except OSError:
- return path
- if len(np) < len(str(path)):
- path = np
- return path
-
+
+ def _makepath(self, path):
+ if not self.abspath:
+ try:
+ np = py.path.local().bestrelpath(path)
+ except OSError:
+ return path
+ if len(np) < len(str(path)):
+ path = np
+ return path
+
def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
- traceback = excinfo.traceback
- if self.tbfilter:
- traceback = traceback.filter()
-
+ traceback = excinfo.traceback
+ if self.tbfilter:
+ traceback = traceback.filter()
+
if isinstance(excinfo.value, RecursionError):
- traceback, extraline = self._truncate_recursive_traceback(traceback)
- else:
- extraline = None
-
- last = traceback[-1]
- entries = []
+ 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)
- 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)
-
+ 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.
-
+
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.
- """
- try:
- recursionindex = traceback.recursionindex()
- except Exception as e:
- max_frames = 10
+ """
+ try:
+ recursionindex = traceback.recursionindex()
+ except Exception as e:
+ max_frames = 10
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__,
+ "!!! 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),
- max_frames=max_frames,
- total=len(traceback),
+ 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
- else:
- if recursionindex is not None:
- extraline = "!!! Recursion detected (same locals & position)"
- traceback = traceback[: recursionindex + 1]
- else:
- extraline = None
-
- return traceback, extraline
-
+ else:
+ if recursionindex is not None:
+ extraline = "!!! Recursion detected (same locals & position)"
+ traceback = traceback[: recursionindex + 1]
+ else:
+ extraline = None
+
+ return traceback, extraline
+
def repr_excinfo(
self, excinfo: ExceptionInfo[BaseException]
) -> "ExceptionChainRepr":
@@ -913,7 +913,7 @@ class FormattedExcinfo:
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__
@@ -937,46 +937,46 @@ class FormattedExcinfo:
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.
+ # FYI this is called from pytest-xdist's serialization of exception
+ # information.
io = StringIO()
tw = TerminalWriter(file=io)
- self.toterminal(tw)
- return io.getvalue().strip()
-
+ self.toterminal(tw)
+ return io.getvalue().strip()
+
def __repr__(self) -> str:
return "<{} instance at {:0x}>".format(self.__class__, id(self))
-
+
def toterminal(self, tw: TerminalWriter) -> None:
raise NotImplementedError()
-
+
# This class is abstract -- only subclasses are instantiated.
@attr.s(eq=False)
-class ExceptionRepr(TerminalRepr):
+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:
- self.sections.append((name, content, sep))
-
+ self.sections.append((name, content, sep))
+
def toterminal(self, tw: TerminalWriter) -> None:
- for name, content, sep in self.sections:
- tw.sep(sep, name)
- tw.line(content)
-
-
+ for name, content, sep in self.sections:
+ tw.sep(sep, name)
+ tw.line(content)
+
+
@attr.s(eq=False)
-class ExceptionChainRepr(ExceptionRepr):
+class ExceptionChainRepr(ExceptionRepr):
chain = attr.ib(
type=Sequence[
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
@@ -985,81 +985,81 @@ class ExceptionChainRepr(ExceptionRepr):
def __attrs_post_init__(self) -> None:
super().__attrs_post_init__()
- # reprcrash and reprtraceback of the outermost (the newest) exception
+ # reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain.
self.reprtraceback = self.chain[-1][0]
self.reprcrash = self.chain[-1][1]
-
+
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)
+ 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)
-
-
+
+
@attr.s(eq=False)
-class ReprExceptionInfo(ExceptionRepr):
+class ReprExceptionInfo(ExceptionRepr):
reprtraceback = attr.ib(type="ReprTraceback")
reprcrash = attr.ib(type="ReprFileLocation")
-
+
def toterminal(self, tw: TerminalWriter) -> None:
- self.reprtraceback.toterminal(tw)
+ self.reprtraceback.toterminal(tw)
super().toterminal(tw)
-
-
+
+
@attr.s(eq=False)
-class ReprTraceback(TerminalRepr):
+class ReprTraceback(TerminalRepr):
reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]])
extraline = attr.ib(type=Optional[str])
style = attr.ib(type="_TracebackStyle")
- entrysep = "_ "
-
+ entrysep = "_ "
+
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("")
- entry.toterminal(tw)
- if i < len(self.reprentries) - 1:
- next_entry = self.reprentries[i + 1]
- if (
- entry.style == "long"
- or entry.style == "short"
- and next_entry.style == "long"
- ):
- tw.sep(self.entrysep)
-
- if self.extraline:
- tw.line(self.extraline)
-
-
-class ReprTracebackNative(ReprTraceback):
+ for i, entry in enumerate(self.reprentries):
+ if entry.style == "long":
+ tw.line("")
+ entry.toterminal(tw)
+ if i < len(self.reprentries) - 1:
+ next_entry = self.reprentries[i + 1]
+ if (
+ entry.style == "long"
+ or entry.style == "short"
+ and next_entry.style == "long"
+ ):
+ tw.sep(self.entrysep)
+
+ if self.extraline:
+ tw.line(self.extraline)
+
+
+class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines: Sequence[str]) -> None:
- self.style = "native"
- self.reprentries = [ReprEntryNative(tblines)]
- self.extraline = None
-
-
+ self.style = "native"
+ self.reprentries = [ReprEntryNative(tblines)]
+ self.extraline = None
+
+
@attr.s(eq=False)
-class ReprEntryNative(TerminalRepr):
+class ReprEntryNative(TerminalRepr):
lines = attr.ib(type=Sequence[str])
style: "_TracebackStyle" = "native"
-
+
def toterminal(self, tw: TerminalWriter) -> None:
- tw.write("".join(self.lines))
-
-
+ tw.write("".join(self.lines))
+
+
@attr.s(eq=False)
-class ReprEntry(TerminalRepr):
+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.
@@ -1105,82 +1105,82 @@ class ReprEntry(TerminalRepr):
tw.line(line, bold=True, red=True)
def toterminal(self, tw: TerminalWriter) -> None:
- if self.style == "short":
+ if self.style == "short":
assert self.reprfileloc is not None
- self.reprfileloc.toterminal(tw)
+ self.reprfileloc.toterminal(tw)
self._write_entry_lines(tw)
if self.reprlocals:
self.reprlocals.toterminal(tw, indent=" " * 8)
- return
+ return
- if self.reprfuncargs:
- self.reprfuncargs.toterminal(tw)
+ if self.reprfuncargs:
+ self.reprfuncargs.toterminal(tw)
self._write_entry_lines(tw)
- if self.reprlocals:
- tw.line("")
- self.reprlocals.toterminal(tw)
- if self.reprfileloc:
- if self.lines:
- tw.line("")
- self.reprfileloc.toterminal(tw)
-
+ if self.reprlocals:
+ tw.line("")
+ self.reprlocals.toterminal(tw)
+ if self.reprfileloc:
+ if self.lines:
+ tw.line("")
+ self.reprfileloc.toterminal(tw)
+
def __str__(self) -> str:
return "{}\n{}\n{}".format(
"\n".join(self.lines), self.reprlocals, self.reprfileloc
)
-
-
+
+
@attr.s(eq=False)
-class ReprFileLocation(TerminalRepr):
+class ReprFileLocation(TerminalRepr):
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.
- msg = self.message
- i = msg.find("\n")
- if i != -1:
- msg = msg[:i]
- tw.write(self.path, bold=True, red=True)
+ 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}")
-
-
+
+
@attr.s(eq=False)
-class ReprLocals(TerminalRepr):
+class ReprLocals(TerminalRepr):
lines = attr.ib(type=Sequence[str])
-
+
def toterminal(self, tw: TerminalWriter, indent="") -> None:
- for line in self.lines:
+ for line in self.lines:
tw.line(indent + line)
-
-
+
+
@attr.s(eq=False)
-class ReprFuncArgs(TerminalRepr):
+class ReprFuncArgs(TerminalRepr):
args = attr.ib(type=Sequence[Tuple[str, object]])
-
+
def toterminal(self, tw: TerminalWriter) -> None:
- if self.args:
- linesofar = ""
- for name, value in self.args:
+ if self.args:
+ linesofar = ""
+ for name, value in self.args:
ns = f"{name} = {value}"
- if len(ns) + len(linesofar) + 2 > tw.fullwidth:
- if linesofar:
- tw.line(linesofar)
- linesofar = ns
- else:
- if linesofar:
- linesofar += ", " + ns
- else:
- linesofar = ns
- if linesofar:
- tw.line(linesofar)
- tw.line("")
-
-
+ if len(ns) + len(linesofar) + 2 > tw.fullwidth:
+ if linesofar:
+ tw.line(linesofar)
+ linesofar = ns
+ else:
+ if linesofar:
+ linesofar += ", " + ns
+ else:
+ linesofar = ns
+ if linesofar:
+ tw.line(linesofar)
+ tw.line("")
+
+
def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
"""Return source location (path, lineno) for the given object.
@@ -1195,14 +1195,14 @@ def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
if hasattr(obj, "place_as"):
obj = obj.place_as # type: ignore[attr-defined]
- try:
+ 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:
@@ -1211,40 +1211,40 @@ def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
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
+# note: if we need to add more paths than what we have now we should probably use a list
# for better maintenance.
-
+
_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc"))
-# pluggy is either a package or a single module depending on the version
+# 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
-
-
+
+
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
+ * 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.
- raw_filename = entry.frame.code.raw.co_filename
- is_generated = "<" in raw_filename and ">" in raw_filename
- if is_generated:
- return False
+ 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
+ # 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)
diff --git a/contrib/python/pytest/py3/_pytest/_code/source.py b/contrib/python/pytest/py3/_pytest/_code/source.py
index 56bf0fdc20..6f54057c0a 100644
--- a/contrib/python/pytest/py3/_pytest/_code/source.py
+++ b/contrib/python/pytest/py3/_pytest/_code/source.py
@@ -1,10 +1,10 @@
-import ast
-import inspect
-import textwrap
-import tokenize
+import ast
+import inspect
+import textwrap
+import tokenize
import types
-import warnings
-from bisect import bisect_right
+import warnings
+from bisect import bisect_right
from typing import Iterable
from typing import Iterator
from typing import List
@@ -12,14 +12,14 @@ 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.
-
+
When using Source(...), the source lines are deindented.
- """
-
+ """
+
def __init__(self, obj: object = None) -> None:
if not obj:
self.lines: List[str] = []
@@ -36,15 +36,15 @@ class Source:
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:
...
@@ -54,81 +54,81 @@ class Source:
...
def __getitem__(self, key: Union[int, slice]) -> Union[str, "Source"]:
- if isinstance(key, int):
- return self.lines[key]
- else:
- if key.step not in (None, 1):
- raise IndexError("cannot slice a Source with a step")
- newsource = Source()
- newsource.lines = self.lines[key.start : key.stop]
- return newsource
-
+ if isinstance(key, int):
+ return self.lines[key]
+ else:
+ if key.step not in (None, 1):
+ raise IndexError("cannot slice a Source with a step")
+ newsource = Source()
+ newsource.lines = self.lines[key.start : key.stop]
+ return newsource
+
def __iter__(self) -> Iterator[str]:
return iter(self.lines)
def __len__(self) -> int:
- return len(self.lines)
-
+ return len(self.lines)
+
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
- while end > start and not self.lines[end - 1].strip():
- end -= 1
- source = Source()
- source.lines[:] = self.lines[start:end]
- return source
-
+ start, end = 0, len(self)
+ while start < end and not self.lines[start].strip():
+ start += 1
+ while end > start and not self.lines[end - 1].strip():
+ end -= 1
+ source = 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."""
- newsource = Source()
- newsource.lines = [(indent + line) for line in self.lines]
- return newsource
-
+ 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)."""
- start, end = self.getstatementrange(lineno)
- return self[start:end]
-
+ 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."""
- if not (0 <= lineno < len(self)):
- raise IndexError("lineno out of range")
- ast, start, end = getstatementrange_ast(lineno, self)
- return start, end
-
+ 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."""
- newsource = Source()
- newsource.lines[:] = deindent(self.lines)
- return newsource
-
+ newsource = Source()
+ newsource.lines[:] = deindent(self.lines)
+ return newsource
+
def __str__(self) -> str:
- return "\n".join(self.lines)
-
-
-#
-# helper functions
-#
-
-
+ return "\n".join(self.lines)
+
+
+#
+# helper functions
+#
+
+
def findsource(obj) -> Tuple[Optional[Source], int]:
- try:
- sourcelines, lineno = inspect.findsource(obj)
+ try:
+ sourcelines, lineno = inspect.findsource(obj)
except Exception:
- return None, -1
- source = Source()
- source.lines = [line.rstrip() for line in sourcelines]
- return source, lineno
-
-
+ 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."""
- try:
+ try:
return obj.__code__ # type: ignore[attr-defined,no-any-return]
except AttributeError:
pass
@@ -137,76 +137,76 @@ def getrawcode(obj: object, trycall: bool = True) -> types.CodeType:
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]:
- return textwrap.dedent("\n".join(lines)).splitlines()
-
-
+ 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] = []
- for x in ast.walk(node):
- if isinstance(x, (ast.stmt, ast.ExceptHandler)):
- values.append(x.lineno - 1)
- for name in ("finalbody", "orelse"):
+ 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)
- if val:
+ if val:
# Treat the finally/orelse part as its own statement.
- values.append(val[0].lineno - 1 - 1)
- values.sort()
- insert_index = bisect_right(values, lineno)
- start = values[insert_index - 1]
- if insert_index >= len(values):
- end = None
- else:
- end = values[insert_index]
- return start, end
-
-
+ values.append(val[0].lineno - 1 - 1)
+ values.sort()
+ insert_index = bisect_right(values, lineno)
+ start = values[insert_index - 1]
+ if insert_index >= len(values):
+ end = None
+ else:
+ end = values[insert_index]
+ return start, end
+
+
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:
+ if astnode is None:
+ content = str(source)
+ # See #4260:
# Don't produce duplicate warnings when compiling source to find AST.
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
astnode = ast.parse(content, "source", "exec")
-
- start, end = get_statement_startend2(lineno, astnode)
+
+ start, end = get_statement_startend2(lineno, astnode)
# 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
- if end is None:
- end = len(source.lines)
-
- if end > start + 1:
+ # - ast-parsing strips comments
+ # - there might be empty lines
+ # - we might have lesser indented code blocks at the end
+ if end is None:
+ 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.
- block_finder = inspect.BlockFinder()
+ block_finder = inspect.BlockFinder()
# 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:
- for tok in tokenize.generate_tokens(lambda: next(it)):
- block_finder.tokeneater(*tok)
- except (inspect.EndOfBlock, IndentationError):
- end = block_finder.last + start
- except Exception:
- pass
-
+ block_finder.started = source.lines[start][0].isspace()
+ it = ((x + "\n") for x in source.lines[start:end])
+ try:
+ for tok in tokenize.generate_tokens(lambda: next(it)):
+ block_finder.tokeneater(*tok)
+ except (inspect.EndOfBlock, IndentationError):
+ end = block_finder.last + start
+ except Exception:
+ pass
+
# 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:
- end -= 1
- else:
- break
- return astnode, start, end
+ while end:
+ line = source.lines[end - 1].lstrip()
+ if line.startswith("#") or not line:
+ end -= 1
+ else:
+ break
+ return astnode, start, end