mirror of
https://github.com/python/cpython.git
synced 2024-11-24 00:38:00 +01:00
gh-112332: Deprecate TracebackException.exc_type, add exc_type_str. (#112333)
This commit is contained in:
parent
2df26d8348
commit
2c68011780
@ -287,6 +287,14 @@ capture data for later printing in a lightweight fashion.
|
||||
|
||||
The class of the original traceback.
|
||||
|
||||
.. deprecated:: 3.13
|
||||
|
||||
.. attribute:: exc_type_str
|
||||
|
||||
String display of the class of the original exception.
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
.. attribute:: filename
|
||||
|
||||
For syntax errors - the file name where the error occurred.
|
||||
|
@ -318,6 +318,12 @@ traceback
|
||||
to format the nested exceptions of a :exc:`BaseExceptionGroup` instance, recursively.
|
||||
(Contributed by Irit Katriel in :gh:`105292`.)
|
||||
|
||||
* Add the field *exc_type_str* to :class:`~traceback.TracebackException`, which
|
||||
holds a string display of the *exc_type*. Deprecate the field *exc_type*
|
||||
which holds the type object itself. Add parameter *save_exc_type* (default
|
||||
``True``) to indicate whether ``exc_type`` should be saved.
|
||||
(Contributed by Irit Katriel in :gh:`112332`.)
|
||||
|
||||
typing
|
||||
------
|
||||
|
||||
@ -377,6 +383,11 @@ Deprecated
|
||||
security and functionality bugs. This includes removal of the ``--cgi``
|
||||
flag to the ``python -m http.server`` command line in 3.15.
|
||||
|
||||
* :mod:`traceback`:
|
||||
|
||||
* The field *exc_type* of :class:`traceback.TracebackException` is
|
||||
deprecated. Use *exc_type_str* instead.
|
||||
|
||||
* :mod:`typing`:
|
||||
|
||||
* Creating a :class:`typing.NamedTuple` class using keyword arguments to denote
|
||||
|
@ -2715,9 +2715,9 @@ class Unrepresentable:
|
||||
|
||||
class TestTracebackException(unittest.TestCase):
|
||||
|
||||
def test_smoke(self):
|
||||
def do_test_smoke(self, exc, expected_type_str):
|
||||
try:
|
||||
1/0
|
||||
raise exc
|
||||
except Exception as e:
|
||||
exc_obj = e
|
||||
exc = traceback.TracebackException.from_exception(e)
|
||||
@ -2727,9 +2727,23 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(None, exc.__context__)
|
||||
self.assertEqual(False, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(expected_type_str, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_smoke_builtin(self):
|
||||
self.do_test_smoke(ValueError(42), 'ValueError')
|
||||
|
||||
def test_smoke_user_exception(self):
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
self.do_test_smoke(
|
||||
MyException('bad things happened'),
|
||||
('test.test_traceback.TestTracebackException.'
|
||||
'test_smoke_user_exception.<locals>.MyException'))
|
||||
|
||||
def test_from_exception(self):
|
||||
# Check all the parameters are accepted.
|
||||
def foo():
|
||||
@ -2750,7 +2764,9 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(None, exc.__context__)
|
||||
self.assertEqual(False, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_cause(self):
|
||||
@ -2772,7 +2788,9 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(exc_context, exc.__context__)
|
||||
self.assertEqual(True, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_context(self):
|
||||
@ -2792,7 +2810,9 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(exc_context, exc.__context__)
|
||||
self.assertEqual(False, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_long_context_chain(self):
|
||||
@ -2837,7 +2857,9 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(None, exc.__context__)
|
||||
self.assertEqual(True, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_compact_no_cause(self):
|
||||
@ -2857,9 +2879,22 @@ class TestTracebackException(unittest.TestCase):
|
||||
self.assertEqual(exc_context, exc.__context__)
|
||||
self.assertEqual(False, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertEqual(type(exc_obj), exc.exc_type)
|
||||
self.assertEqual(type(exc_obj).__name__, exc.exc_type_str)
|
||||
self.assertEqual(str(exc_obj), str(exc))
|
||||
|
||||
def test_no_save_exc_type(self):
|
||||
try:
|
||||
1/0
|
||||
except Exception as e:
|
||||
exc = e
|
||||
|
||||
te = traceback.TracebackException.from_exception(
|
||||
exc, save_exc_type=False)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertIsNone(te.exc_type)
|
||||
|
||||
def test_no_refs_to_exception_and_traceback_objects(self):
|
||||
try:
|
||||
1/0
|
||||
|
@ -5,6 +5,7 @@ import itertools
|
||||
import linecache
|
||||
import sys
|
||||
import textwrap
|
||||
import warnings
|
||||
from contextlib import suppress
|
||||
|
||||
__all__ = ['extract_stack', 'extract_tb', 'format_exception',
|
||||
@ -719,7 +720,8 @@ class TracebackException:
|
||||
- :attr:`__suppress_context__` The *__suppress_context__* value from the
|
||||
original exception.
|
||||
- :attr:`stack` A `StackSummary` representing the traceback.
|
||||
- :attr:`exc_type` The class of the original traceback.
|
||||
- :attr:`exc_type` (deprecated) The class of the original traceback.
|
||||
- :attr:`exc_type_str` String display of exc_type
|
||||
- :attr:`filename` For syntax errors - the filename where the error
|
||||
occurred.
|
||||
- :attr:`lineno` For syntax errors - the linenumber where the error
|
||||
@ -737,7 +739,7 @@ class TracebackException:
|
||||
|
||||
def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
|
||||
lookup_lines=True, capture_locals=False, compact=False,
|
||||
max_group_width=15, max_group_depth=10, _seen=None):
|
||||
max_group_width=15, max_group_depth=10, save_exc_type=True, _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.
|
||||
@ -754,12 +756,23 @@ class TracebackException:
|
||||
_walk_tb_with_full_positions(exc_traceback),
|
||||
limit=limit, lookup_lines=lookup_lines,
|
||||
capture_locals=capture_locals)
|
||||
self.exc_type = exc_type
|
||||
|
||||
self._exc_type = exc_type if save_exc_type else None
|
||||
|
||||
# Capture now to permit freeing resources: only complication is in the
|
||||
# unofficial API _format_final_exc_line
|
||||
self._str = _safe_string(exc_value, 'exception')
|
||||
self.__notes__ = getattr(exc_value, '__notes__', None)
|
||||
|
||||
self._is_syntax_error = False
|
||||
self._have_exc_type = exc_type is not None
|
||||
if exc_type is not None:
|
||||
self.exc_type_qualname = exc_type.__qualname__
|
||||
self.exc_type_module = exc_type.__module__
|
||||
else:
|
||||
self.exc_type_qualname = None
|
||||
self.exc_type_module = None
|
||||
|
||||
if exc_type and issubclass(exc_type, SyntaxError):
|
||||
# Handle SyntaxError's specially
|
||||
self.filename = exc_value.filename
|
||||
@ -771,6 +784,7 @@ class TracebackException:
|
||||
self.offset = exc_value.offset
|
||||
self.end_offset = exc_value.end_offset
|
||||
self.msg = exc_value.msg
|
||||
self._is_syntax_error = True
|
||||
elif exc_type and issubclass(exc_type, ImportError) and \
|
||||
getattr(exc_value, "name_from", None) is not None:
|
||||
wrong_name = getattr(exc_value, "name_from", None)
|
||||
@ -869,6 +883,24 @@ class TracebackException:
|
||||
"""Create a TracebackException from an exception."""
|
||||
return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def exc_type(self):
|
||||
warnings.warn('Deprecated in 3.13. Use exc_type_str instead.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self._exc_type
|
||||
|
||||
@property
|
||||
def exc_type_str(self):
|
||||
if not self._have_exc_type:
|
||||
return None
|
||||
stype = self.exc_type_qualname
|
||||
smod = self.exc_type_module
|
||||
if smod not in ("__main__", "builtins"):
|
||||
if not isinstance(smod, str):
|
||||
smod = "<unknown>"
|
||||
stype = smod + '.' + stype
|
||||
return stype
|
||||
|
||||
def _load_lines(self):
|
||||
"""Private API. force all lines in the stack to be loaded."""
|
||||
for frame in self.stack:
|
||||
@ -901,18 +933,12 @@ class TracebackException:
|
||||
"""
|
||||
|
||||
indent = 3 * _depth * ' '
|
||||
if self.exc_type is None:
|
||||
if not self._have_exc_type:
|
||||
yield indent + _format_final_exc_line(None, self._str)
|
||||
return
|
||||
|
||||
stype = self.exc_type.__qualname__
|
||||
smod = self.exc_type.__module__
|
||||
if smod not in ("__main__", "builtins"):
|
||||
if not isinstance(smod, str):
|
||||
smod = "<unknown>"
|
||||
stype = smod + '.' + stype
|
||||
|
||||
if not issubclass(self.exc_type, SyntaxError):
|
||||
stype = self.exc_type_str
|
||||
if not self._is_syntax_error:
|
||||
if _depth > 0:
|
||||
# Nested exceptions needs correct handling of multiline messages.
|
||||
formatted = _format_final_exc_line(
|
||||
|
@ -0,0 +1,2 @@
|
||||
Deprecate the ``exc_type`` field of :class:`traceback.TracebackException`.
|
||||
Add ``exc_type_str`` to replace it.
|
Loading…
Reference in New Issue
Block a user