bpo-26389: Allow passing an exception object in the traceback module (GH-22610)

The format_exception(), format_exception_only(), and
print_exception() functions can now take an exception object as a positional-only argument.

Co-Authored-By: Matthias Bussonnier <bussonniermatthias@gmail.com>
This commit is contained in:
Zackery Spytz 2020-11-05 15:18:44 -07:00 committed by GitHub
parent dc42af8fd1
commit 91e93794d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 24 deletions

View File

@ -36,7 +36,8 @@ The module defines the following functions:
Added negative *limit* support.
.. function:: print_exception(etype, value, tb, limit=None, file=None, chain=True)
.. function:: print_exception(exc, /[, value, tb], limit=None, \
file=None, chain=True)
Print exception information and stack trace entries from traceback object
*tb* to *file*. This differs from :func:`print_tb` in the following
@ -45,7 +46,7 @@ The module defines the following functions:
* if *tb* is not ``None``, it prints a header ``Traceback (most recent
call last):``
* it prints the exception *etype* and *value* after the stack trace
* it prints the exception type and *value* after the stack trace
.. index:: single: ^ (caret); marker
@ -53,6 +54,10 @@ The module defines the following functions:
format, it prints the line where the syntax error occurred with a caret
indicating the approximate position of the error.
Since Python 3.10, instead of passing *value* and *tb*, an exception object
can be passed as the first argument. If *value* and *tb* are provided, the
first argument is ignored in order to provide backwards compatibility.
The optional *limit* argument has the same meaning as for :func:`print_tb`.
If *chain* is true (the default), then chained exceptions (the
:attr:`__cause__` or :attr:`__context__` attributes of the exception) will be
@ -62,6 +67,10 @@ The module defines the following functions:
.. versionchanged:: 3.5
The *etype* argument is ignored and inferred from the type of *value*.
.. versionchanged:: 3.10
The *etype* parameter has been renamed to *exc* and is now
positional-only.
.. function:: print_exc(limit=None, file=None, chain=True)
@ -121,18 +130,26 @@ The module defines the following functions:
text line is not ``None``.
.. function:: format_exception_only(etype, value)
.. function:: format_exception_only(exc, /[, value])
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. Normally,
the list contains a single string; however, for :exc:`SyntaxError`
exceptions, it contains several lines that (when printed) display detailed
information about where the syntax error occurred. The message indicating
which exception occurred is the always last string in the list.
Format the exception part of a traceback using an exception value such as
given by ``sys.last_value``. The return value is a list of strings, each
ending in a newline. Normally, the list contains a single string; however,
for :exc:`SyntaxError` exceptions, it contains several lines that (when
printed) display detailed information about where the syntax error occurred.
The message indicating which exception occurred is the always last string in
the list.
Since Python 3.10, instead of passing *value*, an exception object
can be passed as the first argument. If *value* is provided, the first
argument is ignored in order to provide backwards compatibility.
.. versionchanged:: 3.10
The *etype* parameter has been renamed to *exc* and is now
positional-only.
.. function:: format_exception(etype, value, tb, limit=None, chain=True)
.. function:: format_exception(exc, /[, value, tb], limit=None, chain=True)
Format a stack trace and the exception information. The arguments have the
same meaning as the corresponding arguments to :func:`print_exception`. The
@ -143,6 +160,10 @@ The module defines the following functions:
.. versionchanged:: 3.5
The *etype* argument is ignored and inferred from the type of *value*.
.. versionchanged:: 3.10
This function's behavior and signature were modified to match
:func:`print_exception`.
.. function:: format_exc(limit=None, chain=True)

View File

@ -232,6 +232,15 @@ retrieve the functions set by :func:`threading.settrace` and
:func:`threading.setprofile` respectively.
(Contributed by Mario Corchero in :issue:`42251`.)
traceback
---------
The :func:`~traceback.format_exception`,
:func:`~traceback.format_exception_only`, and
:func:`~traceback.print_exception` functions can now take an exception object
as a positional-only argument.
(Contributed by Zackery Spytz and Matthias Bussonnier in :issue:`26389`.)
types
-----
@ -328,6 +337,15 @@ This section lists previously described changes and other bugfixes
that may require changes to your code.
Changes in the Python API
-------------------------
* The *etype* parameters of the :func:`~traceback.format_exception`,
:func:`~traceback.format_exception_only`, and
:func:`~traceback.print_exception` functions in the :mod:`traceback` module
have been renamed to *exc*.
(Contributed by Zackery Spytz and Matthias Bussonnier in :issue:`26389`.)
Build Changes
=============

View File

@ -212,6 +212,26 @@ class TracebackCases(unittest.TestCase):
)
self.assertEqual(output.getvalue(), "Exception: projector\n")
def test_print_exception_exc(self):
output = StringIO()
traceback.print_exception(Exception("projector"), file=output)
self.assertEqual(output.getvalue(), "Exception: projector\n")
def test_format_exception_exc(self):
e = Exception("projector")
output = traceback.format_exception(e)
self.assertEqual(output, ["Exception: projector\n"])
with self.assertRaisesRegex(ValueError, 'Both or neither'):
traceback.format_exception(e.__class__, e)
with self.assertRaisesRegex(ValueError, 'Both or neither'):
traceback.format_exception(e.__class__, tb=e.__traceback__)
with self.assertRaisesRegex(TypeError, 'positional-only'):
traceback.format_exception(exc=e)
def test_format_exception_only_exc(self):
output = traceback.format_exception_only(Exception("projector"))
self.assertEqual(output, ["Exception: projector\n"])
class TracebackFormatTests(unittest.TestCase):

View File

@ -84,7 +84,19 @@ _context_message = (
"another exception occurred:\n\n")
def print_exception(etype, value, tb, limit=None, file=None, chain=True):
_sentinel = object()
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:
return exc, exc.__traceback__
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,9 +107,7 @@ 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(
@ -105,7 +115,8 @@ def print_exception(etype, value, tb, limit=None, file=None, chain=True):
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 +125,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).
value, tb = _parse_value_tb(exc, value, tb)
return list(TracebackException(
type(value), value, tb, limit=limit).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 +144,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
return list(TracebackException(
type(value), value, None).format_exception_only())
# -- not official API but folk probably use these two functions.

View File

@ -0,0 +1,4 @@
The :func:`traceback.format_exception`,
:func:`traceback.format_exception_only`, and
:func:`traceback.print_exception` functions can now take an exception object
as a positional-only argument.