aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Lib/traceback.py
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.ru>2022-04-18 12:39:32 +0300
committershadchin <shadchin@yandex-team.ru>2022-04-18 12:39:32 +0300
commitd4be68e361f4258cf0848fc70018dfe37a2acc24 (patch)
tree153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Lib/traceback.py
parent260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff)
downloadydb-d4be68e361f4258cf0848fc70018dfe37a2acc24.tar.gz
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Lib/traceback.py')
-rw-r--r--contrib/tools/python3/src/Lib/traceback.py223
1 files changed, 137 insertions, 86 deletions
diff --git a/contrib/tools/python3/src/Lib/traceback.py b/contrib/tools/python3/src/Lib/traceback.py
index c771a137cd..d6a010f415 100644
--- a/contrib/tools/python3/src/Lib/traceback.py
+++ b/contrib/tools/python3/src/Lib/traceback.py
@@ -84,7 +84,25 @@ _context_message = (
"another exception occurred:\n\n")
-def print_exception(etype, value, tb, limit=None, file=None, chain=True):
+class _Sentinel:
+ def __repr__(self):
+ return "<implicit>"
+
+_sentinel = _Sentinel()
+
+def _parse_value_tb(exc, value, tb):
+ if (value is _sentinel) != (tb is _sentinel):
+ raise ValueError("Both or neither of value and tb must be given")
+ if value is tb is _sentinel:
+ if exc is not None:
+ return exc, exc.__traceback__
+ else:
+ return None, None
+ return value, tb
+
+
+def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
+ file=None, chain=True):
"""Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
This differs from print_tb() in the following ways: (1) if
@@ -95,17 +113,16 @@ def print_exception(etype, value, tb, limit=None, file=None, chain=True):
occurred with a caret on the next line indicating the approximate
position of the error.
"""
- # format_exception has ignored etype for some time, and code such as cgitb
- # passes in bogus values as a result. For compatibility with such code we
- # ignore it here (rather than in the new TracebackException API).
+ value, tb = _parse_value_tb(exc, value, tb)
if file is None:
file = sys.stderr
- for line in TracebackException(
- type(value), value, tb, limit=limit).format(chain=chain):
+ te = TracebackException(type(value), value, tb, limit=limit, compact=True)
+ for line in te.format(chain=chain):
print(line, file=file, end="")
-def format_exception(etype, value, tb, limit=None, chain=True):
+def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
+ chain=True):
"""Format a stack trace and the exception information.
The arguments have the same meaning as the corresponding arguments
@@ -114,19 +131,15 @@ def format_exception(etype, value, tb, limit=None, chain=True):
these lines are concatenated and printed, exactly the same text is
printed as does print_exception().
"""
- # format_exception has ignored etype for some time, and code such as cgitb
- # passes in bogus values as a result. For compatibility with such code we
- # ignore it here (rather than in the new TracebackException API).
- return list(TracebackException(
- type(value), value, tb, limit=limit).format(chain=chain))
+ value, tb = _parse_value_tb(exc, value, tb)
+ te = TracebackException(type(value), value, tb, limit=limit, compact=True)
+ return list(te.format(chain=chain))
-def format_exception_only(etype, value):
+def format_exception_only(exc, /, value=_sentinel):
"""Format the exception part of a traceback.
- The arguments are the exception type and value such as given by
- sys.last_type and sys.last_value. The return value is a list of
- strings, each ending in a newline.
+ The return value is a list of strings, each ending in a newline.
Normally, the list contains a single string; however, for
SyntaxError exceptions, it contains several lines that (when
@@ -137,7 +150,10 @@ def format_exception_only(etype, value):
string in the list.
"""
- return list(TracebackException(etype, value, None).format_exception_only())
+ if value is _sentinel:
+ value = exc
+ te = TracebackException(type(value), value, None, compact=True)
+ return list(te.format_exception_only())
# -- not official API but folk probably use these two functions.
@@ -285,9 +301,10 @@ class FrameSummary:
@property
def line(self):
if self._line is None:
- self._line = linecache.getline(self.filename, self.lineno).strip()
- return self._line
-
+ if self.lineno is None:
+ return None
+ self._line = linecache.getline(self.filename, self.lineno)
+ return self._line.strip()
def walk_stack(f):
"""Walk a stack yielding the frame and line number for each frame.
@@ -458,61 +475,29 @@ class TracebackException:
occurred.
- :attr:`lineno` For syntax errors - the linenumber where the error
occurred.
+ - :attr:`end_lineno` For syntax errors - the end linenumber where the error
+ occurred. Can be `None` if not present.
- :attr:`text` For syntax errors - the text where the error
occurred.
- :attr:`offset` For syntax errors - the offset into the text where the
error occurred.
+ - :attr:`end_offset` For syntax errors - the offset into the text where the
+ error occurred. Can be `None` if not present.
- :attr:`msg` For syntax errors - the compiler error message.
"""
def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
- lookup_lines=True, capture_locals=False, _seen=None):
+ lookup_lines=True, capture_locals=False, compact=False,
+ _seen=None):
# NB: we need to accept exc_traceback, exc_value, exc_traceback to
# permit backwards compat with the existing API, otherwise we
# need stub thunk objects just to glue it together.
# Handle loops in __cause__ or __context__.
+ is_recursive_call = _seen is not None
if _seen is None:
_seen = set()
_seen.add(id(exc_value))
- # Gracefully handle (the way Python 2.4 and earlier did) the case of
- # being called with no type or value (None, None, None).
- self._truncated = False
- try:
- if (exc_value and exc_value.__cause__ is not None
- and id(exc_value.__cause__) not in _seen):
- cause = TracebackException(
- type(exc_value.__cause__),
- exc_value.__cause__,
- exc_value.__cause__.__traceback__,
- limit=limit,
- lookup_lines=False,
- capture_locals=capture_locals,
- _seen=_seen)
- else:
- cause = None
- if (exc_value and exc_value.__context__ is not None
- and id(exc_value.__context__) not in _seen):
- context = TracebackException(
- type(exc_value.__context__),
- exc_value.__context__,
- exc_value.__context__.__traceback__,
- limit=limit,
- lookup_lines=False,
- capture_locals=capture_locals,
- _seen=_seen)
- else:
- context = None
- except RecursionError:
- # The recursive call to the constructors above
- # may result in a stack overflow for long exception chains,
- # so we must truncate.
- self._truncated = True
- cause = None
- context = None
- self.__cause__ = cause
- self.__context__ = context
- self.__suppress_context__ = \
- exc_value.__suppress_context__ if exc_value else False
+
# TODO: locals.
self.stack = StackSummary.extract(
walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines,
@@ -526,11 +511,60 @@ class TracebackException:
self.filename = exc_value.filename
lno = exc_value.lineno
self.lineno = str(lno) if lno is not None else None
+ end_lno = exc_value.end_lineno
+ self.end_lineno = str(end_lno) if end_lno is not None else None
self.text = exc_value.text
self.offset = exc_value.offset
+ self.end_offset = exc_value.end_offset
self.msg = exc_value.msg
if lookup_lines:
self._load_lines()
+ self.__suppress_context__ = \
+ exc_value.__suppress_context__ if exc_value is not None else False
+
+ # Convert __cause__ and __context__ to `TracebackExceptions`s, use a
+ # queue to avoid recursion (only the top-level call gets _seen == None)
+ if not is_recursive_call:
+ queue = [(self, exc_value)]
+ while queue:
+ te, e = queue.pop()
+ if (e and e.__cause__ is not None
+ and id(e.__cause__) not in _seen):
+ cause = TracebackException(
+ type(e.__cause__),
+ e.__cause__,
+ e.__cause__.__traceback__,
+ limit=limit,
+ lookup_lines=lookup_lines,
+ capture_locals=capture_locals,
+ _seen=_seen)
+ else:
+ cause = None
+
+ if compact:
+ need_context = (cause is None and
+ e is not None and
+ not e.__suppress_context__)
+ else:
+ need_context = True
+ if (e and e.__context__ is not None
+ and need_context and id(e.__context__) not in _seen):
+ context = TracebackException(
+ type(e.__context__),
+ e.__context__,
+ e.__context__.__traceback__,
+ limit=limit,
+ lookup_lines=lookup_lines,
+ capture_locals=capture_locals,
+ _seen=_seen)
+ else:
+ context = None
+ te.__cause__ = cause
+ te.__context__ = context
+ if cause:
+ queue.append((te.__cause__, e.__cause__))
+ if context:
+ queue.append((te.__context__, e.__context__))
@classmethod
def from_exception(cls, exc, *args, **kwargs):
@@ -541,10 +575,6 @@ class TracebackException:
"""Private API. force all lines in the stack to be loaded."""
for frame in self.stack:
frame.line
- if self.__context__:
- self.__context__._load_lines()
- if self.__cause__:
- self.__cause__._load_lines()
def __eq__(self, other):
if isinstance(other, TracebackException):
@@ -602,12 +632,20 @@ class TracebackException:
ltext = rtext.lstrip(' \n\f')
spaces = len(rtext) - len(ltext)
yield ' {}\n'.format(ltext)
- # Convert 1-based column offset to 0-based index into stripped text
- caret = (self.offset or 0) - 1 - spaces
- if caret >= 0:
- # non-space whitespace (likes tabs) must be kept for alignment
- caretspace = ((c if c.isspace() else ' ') for c in ltext[:caret])
- yield ' {}^\n'.format(''.join(caretspace))
+
+ if self.offset is not None:
+ offset = self.offset
+ end_offset = self.end_offset if self.end_offset not in {None, 0} else offset
+ if offset == end_offset or end_offset == -1:
+ end_offset = offset + 1
+
+ # Convert 1-based column offset to 0-based index into stripped text
+ colno = offset - 1 - spaces
+ end_colno = end_offset - 1 - spaces
+ if colno >= 0:
+ # non-space whitespace (likes tabs) must be kept for alignment
+ caretspace = ((c if c.isspace() else ' ') for c in ltext[:colno])
+ yield ' {}{}'.format("".join(caretspace), ('^' * (end_colno - colno) + "\n"))
msg = self.msg or "<no detail available>"
yield "{}: {}{}\n".format(stype, msg, filename_suffix)
@@ -623,19 +661,32 @@ class TracebackException:
The message indicating which exception occurred is always the last
string in the output.
"""
- if chain:
- if self.__cause__ is not None:
- yield from self.__cause__.format(chain=chain)
- yield _cause_message
- elif (self.__context__ is not None and
- not self.__suppress_context__):
- yield from self.__context__.format(chain=chain)
- yield _context_message
- if self._truncated:
- yield (
- 'Chained exceptions have been truncated to avoid '
- 'stack overflow in traceback formatting:\n')
- if self.stack:
- yield 'Traceback (most recent call last):\n'
- yield from self.stack.format()
- yield from self.format_exception_only()
+
+ output = []
+ exc = self
+ while exc:
+ if chain:
+ if exc.__cause__ is not None:
+ chained_msg = _cause_message
+ chained_exc = exc.__cause__
+ elif (exc.__context__ is not None and
+ not exc.__suppress_context__):
+ chained_msg = _context_message
+ chained_exc = exc.__context__
+ else:
+ chained_msg = None
+ chained_exc = None
+
+ output.append((chained_msg, exc))
+ exc = chained_exc
+ else:
+ output.append((None, exc))
+ exc = None
+
+ for msg, exc in reversed(output):
+ if msg is not None:
+ yield msg
+ if exc.stack:
+ yield 'Traceback (most recent call last):\n'
+ yield from exc.stack.format()
+ yield from exc.format_exception_only()