mirror of
https://github.com/python/cpython
synced 2024-09-16 03:20:00 +00:00
gh-102778: Add sys.last_exc, deprecate sys.last_type, sys.last_value,sys.last_traceback (#102779)
This commit is contained in:
parent
039714d00f
commit
e1e9bab006
|
@ -1102,22 +1102,25 @@ always available.
|
||||||
|
|
||||||
.. versionadded:: 3.5
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
.. data:: last_exc
|
||||||
|
|
||||||
|
This variable is not always defined; it is set to the exception instance
|
||||||
|
when an exception is not handled and the interpreter prints an error message
|
||||||
|
and a stack traceback. Its intended use is to allow an interactive user to
|
||||||
|
import a debugger module and engage in post-mortem debugging without having
|
||||||
|
to re-execute the command that caused the error. (Typical use is
|
||||||
|
``import pdb; pdb.pm()`` to enter the post-mortem debugger; see :mod:`pdb`
|
||||||
|
module for more information.)
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
.. data:: last_type
|
.. data:: last_type
|
||||||
last_value
|
last_value
|
||||||
last_traceback
|
last_traceback
|
||||||
|
|
||||||
These three variables are not always defined; they are set when an exception is
|
These three variables are deprecated; use :data:`sys.last_exc` instead.
|
||||||
not handled and the interpreter prints an error message and a stack traceback.
|
They hold the legacy representation of ``sys.last_exc``, as returned
|
||||||
Their intended use is to allow an interactive user to import a debugger module
|
from :func:`exc_info` above.
|
||||||
and engage in post-mortem debugging without having to re-execute the command
|
|
||||||
that caused the error. (Typical use is ``import pdb; pdb.pm()`` to enter the
|
|
||||||
post-mortem debugger; see :mod:`pdb` module for
|
|
||||||
more information.)
|
|
||||||
|
|
||||||
The meaning of the variables is the same as that of the return values from
|
|
||||||
:func:`exc_info` above.
|
|
||||||
|
|
||||||
|
|
||||||
.. data:: maxsize
|
.. data:: maxsize
|
||||||
|
|
||||||
|
|
|
@ -397,6 +397,12 @@ sys
|
||||||
with contributions from Gregory P. Smith [Google] and Mark Shannon
|
with contributions from Gregory P. Smith [Google] and Mark Shannon
|
||||||
in :gh:`96123`.)
|
in :gh:`96123`.)
|
||||||
|
|
||||||
|
* Add :data:`sys.last_exc` which holds the last unhandled exception that
|
||||||
|
was raised (for post-mortem debugging use cases). Deprecate the
|
||||||
|
three fields that have the same information in its legacy form:
|
||||||
|
:data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback`.
|
||||||
|
(Contributed by Irit Katriel in :gh:`102778`.)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
@ -488,6 +494,10 @@ Deprecated
|
||||||
contain the creation time, which is also available in the new ``st_birthtime``
|
contain the creation time, which is also available in the new ``st_birthtime``
|
||||||
field. (Contributed by Steve Dower in :gh:`99726`.)
|
field. (Contributed by Steve Dower in :gh:`99726`.)
|
||||||
|
|
||||||
|
* The :data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback`
|
||||||
|
fields are deprecated. Use :data:`sys.last_exc` instead.
|
||||||
|
(Contributed by Irit Katriel in :gh:`102778`.)
|
||||||
|
|
||||||
Pending Removal in Python 3.13
|
Pending Removal in Python 3.13
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|
|
@ -995,6 +995,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw2));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw2));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(lambda));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(lambda));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_exc));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_node));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_node));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_traceback));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_traceback));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_type));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_type));
|
||||||
|
|
|
@ -481,6 +481,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(kw2)
|
STRUCT_FOR_ID(kw2)
|
||||||
STRUCT_FOR_ID(lambda)
|
STRUCT_FOR_ID(lambda)
|
||||||
STRUCT_FOR_ID(last)
|
STRUCT_FOR_ID(last)
|
||||||
|
STRUCT_FOR_ID(last_exc)
|
||||||
STRUCT_FOR_ID(last_node)
|
STRUCT_FOR_ID(last_node)
|
||||||
STRUCT_FOR_ID(last_traceback)
|
STRUCT_FOR_ID(last_traceback)
|
||||||
STRUCT_FOR_ID(last_type)
|
STRUCT_FOR_ID(last_type)
|
||||||
|
|
1
Include/internal/pycore_runtime_init_generated.h
generated
1
Include/internal/pycore_runtime_init_generated.h
generated
|
@ -987,6 +987,7 @@ extern "C" {
|
||||||
INIT_ID(kw2), \
|
INIT_ID(kw2), \
|
||||||
INIT_ID(lambda), \
|
INIT_ID(lambda), \
|
||||||
INIT_ID(last), \
|
INIT_ID(last), \
|
||||||
|
INIT_ID(last_exc), \
|
||||||
INIT_ID(last_node), \
|
INIT_ID(last_node), \
|
||||||
INIT_ID(last_traceback), \
|
INIT_ID(last_traceback), \
|
||||||
INIT_ID(last_type), \
|
INIT_ID(last_type), \
|
||||||
|
|
|
@ -1296,6 +1296,9 @@ _PyUnicode_InitStaticStrings(void) {
|
||||||
string = &_Py_ID(last);
|
string = &_Py_ID(last);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
PyUnicode_InternInPlace(&string);
|
PyUnicode_InternInPlace(&string);
|
||||||
|
string = &_Py_ID(last_exc);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
PyUnicode_InternInPlace(&string);
|
||||||
string = &_Py_ID(last_node);
|
string = &_Py_ID(last_node);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
PyUnicode_InternInPlace(&string);
|
PyUnicode_InternInPlace(&string);
|
||||||
|
|
|
@ -106,6 +106,7 @@ def showsyntaxerror(self, filename=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
type, value, tb = sys.exc_info()
|
type, value, tb = sys.exc_info()
|
||||||
|
sys.last_exc = value
|
||||||
sys.last_type = type
|
sys.last_type = type
|
||||||
sys.last_value = value
|
sys.last_value = value
|
||||||
sys.last_traceback = tb
|
sys.last_traceback = tb
|
||||||
|
@ -119,7 +120,7 @@ def showsyntaxerror(self, filename=None):
|
||||||
else:
|
else:
|
||||||
# Stuff in the right filename
|
# Stuff in the right filename
|
||||||
value = SyntaxError(msg, (filename, lineno, offset, line))
|
value = SyntaxError(msg, (filename, lineno, offset, line))
|
||||||
sys.last_value = value
|
sys.last_exc = sys.last_value = value
|
||||||
if sys.excepthook is sys.__excepthook__:
|
if sys.excepthook is sys.__excepthook__:
|
||||||
lines = traceback.format_exception_only(type, value)
|
lines = traceback.format_exception_only(type, value)
|
||||||
self.write(''.join(lines))
|
self.write(''.join(lines))
|
||||||
|
@ -138,6 +139,7 @@ def showtraceback(self):
|
||||||
"""
|
"""
|
||||||
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
|
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
|
||||||
sys.last_traceback = last_tb
|
sys.last_traceback = last_tb
|
||||||
|
sys.last_exc = ei[1]
|
||||||
try:
|
try:
|
||||||
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
|
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
|
||||||
if sys.excepthook is sys.__excepthook__:
|
if sys.excepthook is sys.__excepthook__:
|
||||||
|
|
|
@ -118,7 +118,10 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False):
|
||||||
"""Disassemble a traceback (default: last traceback)."""
|
"""Disassemble a traceback (default: last traceback)."""
|
||||||
if tb is None:
|
if tb is None:
|
||||||
try:
|
try:
|
||||||
tb = sys.last_traceback
|
if hasattr(sys, 'last_exc'):
|
||||||
|
tb = sys.last_exc.__traceback__
|
||||||
|
else:
|
||||||
|
tb = sys.last_traceback
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise RuntimeError("no last traceback to disassemble") from None
|
raise RuntimeError("no last traceback to disassemble") from None
|
||||||
while tb.tb_next: tb = tb.tb_next
|
while tb.tb_next: tb = tb.tb_next
|
||||||
|
|
|
@ -19,6 +19,7 @@ def setUpClass(cls):
|
||||||
except NameError:
|
except NameError:
|
||||||
svs.last_type, svs.last_value, svs.last_traceback = (
|
svs.last_type, svs.last_value, svs.last_traceback = (
|
||||||
sys.exc_info())
|
sys.exc_info())
|
||||||
|
svs.last_exc = svs.last_value
|
||||||
|
|
||||||
requires('gui')
|
requires('gui')
|
||||||
cls.root = Tk()
|
cls.root = Tk()
|
||||||
|
@ -27,7 +28,7 @@ def setUpClass(cls):
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
svs = stackviewer.sys
|
svs = stackviewer.sys
|
||||||
del svs.last_traceback, svs.last_type, svs.last_value
|
del svs.last_exc, svs.last_traceback, svs.last_type, svs.last_value
|
||||||
|
|
||||||
cls.root.update_idletasks()
|
cls.root.update_idletasks()
|
||||||
## for id in cls.root.tk.call('after', 'info'):
|
## for id in cls.root.tk.call('after', 'info'):
|
||||||
|
|
|
@ -1367,11 +1367,14 @@ def open_stack_viewer(self, event=None):
|
||||||
if self.interp.rpcclt:
|
if self.interp.rpcclt:
|
||||||
return self.interp.remote_stack_viewer()
|
return self.interp.remote_stack_viewer()
|
||||||
try:
|
try:
|
||||||
sys.last_traceback
|
if hasattr(sys, 'last_exc'):
|
||||||
|
sys.last_exc.__traceback__
|
||||||
|
else:
|
||||||
|
sys.last_traceback
|
||||||
except:
|
except:
|
||||||
messagebox.showerror("No stack trace",
|
messagebox.showerror("No stack trace",
|
||||||
"There is no stack trace yet.\n"
|
"There is no stack trace yet.\n"
|
||||||
"(sys.last_traceback is not defined)",
|
"(sys.last_exc and sys.last_traceback are not defined)",
|
||||||
parent=self.text)
|
parent=self.text)
|
||||||
return
|
return
|
||||||
from idlelib.stackviewer import StackBrowser
|
from idlelib.stackviewer import StackBrowser
|
||||||
|
|
|
@ -239,6 +239,7 @@ def print_exception():
|
||||||
efile = sys.stderr
|
efile = sys.stderr
|
||||||
typ, val, tb = excinfo = sys.exc_info()
|
typ, val, tb = excinfo = sys.exc_info()
|
||||||
sys.last_type, sys.last_value, sys.last_traceback = excinfo
|
sys.last_type, sys.last_value, sys.last_traceback = excinfo
|
||||||
|
sys.last_exc = val
|
||||||
seen = set()
|
seen = set()
|
||||||
|
|
||||||
def print_exc(typ, exc, tb):
|
def print_exc(typ, exc, tb):
|
||||||
|
@ -629,6 +630,7 @@ def stackviewer(self, flist_oid=None):
|
||||||
flist = self.rpchandler.get_remote_proxy(flist_oid)
|
flist = self.rpchandler.get_remote_proxy(flist_oid)
|
||||||
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
|
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
|
||||||
tb = tb.tb_next
|
tb = tb.tb_next
|
||||||
|
sys.last_exc = val
|
||||||
sys.last_type = typ
|
sys.last_type = typ
|
||||||
sys.last_value = val
|
sys.last_value = val
|
||||||
item = stackviewer.StackTreeItem(flist, tb)
|
item = stackviewer.StackTreeItem(flist, tb)
|
||||||
|
|
|
@ -27,7 +27,10 @@ def __init__(self, flist=None, tb=None):
|
||||||
|
|
||||||
def get_stack(self, tb):
|
def get_stack(self, tb):
|
||||||
if tb is None:
|
if tb is None:
|
||||||
tb = sys.last_traceback
|
if hasattr(sys, 'last_exc'):
|
||||||
|
tb = sys.last_exc.__traceback__
|
||||||
|
else:
|
||||||
|
tb = sys.last_traceback
|
||||||
stack = []
|
stack = []
|
||||||
if tb and tb.tb_frame is None:
|
if tb and tb.tb_frame is None:
|
||||||
tb = tb.tb_next
|
tb = tb.tb_next
|
||||||
|
@ -37,11 +40,15 @@ def get_stack(self, tb):
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
def get_exception(self):
|
def get_exception(self):
|
||||||
type = sys.last_type
|
if hasattr(sys, 'last_exc'):
|
||||||
value = sys.last_value
|
typ = type(sys.last_exc)
|
||||||
if hasattr(type, "__name__"):
|
value = sys.last_exc
|
||||||
type = type.__name__
|
else:
|
||||||
s = str(type)
|
typ = sys.last_type
|
||||||
|
value = sys.last_value
|
||||||
|
if hasattr(typ, "__name__"):
|
||||||
|
typ = typ.__name__
|
||||||
|
s = str(typ)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
s = s + ": " + str(value)
|
s = s + ": " + str(value)
|
||||||
return s
|
return s
|
||||||
|
@ -136,6 +143,7 @@ def _stack_viewer(parent): # htest #
|
||||||
except NameError:
|
except NameError:
|
||||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||||
# inject stack trace to sys
|
# inject stack trace to sys
|
||||||
|
sys.last_exc = exc_value
|
||||||
sys.last_type = exc_type
|
sys.last_type = exc_type
|
||||||
sys.last_value = exc_value
|
sys.last_value = exc_value
|
||||||
sys.last_traceback = exc_tb
|
sys.last_traceback = exc_tb
|
||||||
|
@ -143,6 +151,7 @@ def _stack_viewer(parent): # htest #
|
||||||
StackBrowser(top, flist=flist, top=top, tb=exc_tb)
|
StackBrowser(top, flist=flist, top=top, tb=exc_tb)
|
||||||
|
|
||||||
# restore sys to original state
|
# restore sys to original state
|
||||||
|
del sys.last_exc
|
||||||
del sys.last_type
|
del sys.last_type
|
||||||
del sys.last_value
|
del sys.last_value
|
||||||
del sys.last_traceback
|
del sys.last_traceback
|
||||||
|
|
|
@ -1739,7 +1739,11 @@ def post_mortem(t=None):
|
||||||
|
|
||||||
def pm():
|
def pm():
|
||||||
"""Enter post-mortem debugging of the traceback found in sys.last_traceback."""
|
"""Enter post-mortem debugging of the traceback found in sys.last_traceback."""
|
||||||
post_mortem(sys.last_traceback)
|
if hasattr(sys, 'last_exc'):
|
||||||
|
tb = sys.last_exc.__traceback__
|
||||||
|
else:
|
||||||
|
tb = sys.last_traceback
|
||||||
|
post_mortem(tb)
|
||||||
|
|
||||||
|
|
||||||
# Main program for testing
|
# Main program for testing
|
||||||
|
|
|
@ -4799,7 +4799,7 @@
|
||||||
'pdb.pm()\n'
|
'pdb.pm()\n'
|
||||||
'\n'
|
'\n'
|
||||||
' Enter post-mortem debugging of the traceback found in\n'
|
' Enter post-mortem debugging of the traceback found in\n'
|
||||||
' "sys.last_traceback".\n'
|
' "sys.last_exc".\n'
|
||||||
'\n'
|
'\n'
|
||||||
'The "run*" functions and "set_trace()" are aliases for '
|
'The "run*" functions and "set_trace()" are aliases for '
|
||||||
'instantiating\n'
|
'instantiating\n'
|
||||||
|
@ -13858,7 +13858,7 @@
|
||||||
'if\n'
|
'if\n'
|
||||||
' the interpreter is interactive, it is also made available to '
|
' the interpreter is interactive, it is also made available to '
|
||||||
'the\n'
|
'the\n'
|
||||||
' user as "sys.last_traceback".\n'
|
' user as "sys.last_exc".\n'
|
||||||
'\n'
|
'\n'
|
||||||
' For explicitly created tracebacks, it is up to the creator '
|
' For explicitly created tracebacks, it is up to the creator '
|
||||||
'of\n'
|
'of\n'
|
||||||
|
|
|
@ -1026,6 +1026,10 @@ def test_disassemble_try_finally(self):
|
||||||
self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst)
|
self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst)
|
||||||
|
|
||||||
def test_dis_none(self):
|
def test_dis_none(self):
|
||||||
|
try:
|
||||||
|
del sys.last_exc
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
del sys.last_traceback
|
del sys.last_traceback
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -1043,7 +1047,7 @@ def test_dis_traceback(self):
|
||||||
1/0
|
1/0
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tb = e.__traceback__
|
tb = e.__traceback__
|
||||||
sys.last_traceback = tb
|
sys.last_exc = e
|
||||||
|
|
||||||
tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti)
|
tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti)
|
||||||
self.do_disassembly_test(None, tb_dis, True)
|
self.do_disassembly_test(None, tb_dis, True)
|
||||||
|
@ -1900,6 +1904,10 @@ def test_findlabels(self):
|
||||||
|
|
||||||
class TestDisTraceback(DisTestBase):
|
class TestDisTraceback(DisTestBase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
try: # We need to clean up existing tracebacks
|
||||||
|
del sys.last_exc
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
try: # We need to clean up existing tracebacks
|
try: # We need to clean up existing tracebacks
|
||||||
del sys.last_traceback
|
del sys.last_traceback
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -45,7 +45,9 @@ def test_widget_destroy(self):
|
||||||
# value which causes the tracing callback to be called and then
|
# value which causes the tracing callback to be called and then
|
||||||
# it tries calling instance attributes not yet defined.
|
# it tries calling instance attributes not yet defined.
|
||||||
ttk.LabeledScale(self.root, variable=myvar)
|
ttk.LabeledScale(self.root, variable=myvar)
|
||||||
if hasattr(sys, 'last_type'):
|
if hasattr(sys, 'last_exc'):
|
||||||
|
self.assertNotEqual(type(sys.last_exc), tkinter.TclError)
|
||||||
|
elif hasattr(sys, 'last_type'):
|
||||||
self.assertNotEqual(sys.last_type, tkinter.TclError)
|
self.assertNotEqual(sys.last_type, tkinter.TclError)
|
||||||
|
|
||||||
def test_initialization(self):
|
def test_initialization(self):
|
||||||
|
|
|
@ -2400,6 +2400,7 @@ def report_callback_exception(self, exc, val, tb):
|
||||||
should when sys.stderr is None."""
|
should when sys.stderr is None."""
|
||||||
import traceback
|
import traceback
|
||||||
print("Exception in Tkinter callback", file=sys.stderr)
|
print("Exception in Tkinter callback", file=sys.stderr)
|
||||||
|
sys.last_exc = val
|
||||||
sys.last_type = exc
|
sys.last_type = exc
|
||||||
sys.last_value = val
|
sys.last_value = val
|
||||||
sys.last_traceback = tb
|
sys.last_traceback = tb
|
||||||
|
|
|
@ -179,7 +179,7 @@ def _safe_string(value, what, func=str):
|
||||||
# --
|
# --
|
||||||
|
|
||||||
def print_exc(limit=None, file=None, chain=True):
|
def print_exc(limit=None, file=None, chain=True):
|
||||||
"""Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
|
"""Shorthand for 'print_exception(*sys.exc_info(), limit, file, chain)'."""
|
||||||
print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
|
print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
|
||||||
|
|
||||||
def format_exc(limit=None, chain=True):
|
def format_exc(limit=None, chain=True):
|
||||||
|
@ -187,12 +187,16 @@ def format_exc(limit=None, chain=True):
|
||||||
return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))
|
return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))
|
||||||
|
|
||||||
def print_last(limit=None, file=None, chain=True):
|
def print_last(limit=None, file=None, chain=True):
|
||||||
"""This is a shorthand for 'print_exception(sys.last_type,
|
"""This is a shorthand for 'print_exception(sys.last_exc, limit, file, chain)'."""
|
||||||
sys.last_value, sys.last_traceback, limit, file)'."""
|
if not hasattr(sys, "last_exc") and not hasattr(sys, "last_type"):
|
||||||
if not hasattr(sys, "last_type"):
|
|
||||||
raise ValueError("no last exception")
|
raise ValueError("no last exception")
|
||||||
print_exception(sys.last_type, sys.last_value, sys.last_traceback,
|
|
||||||
limit, file, chain)
|
if hasattr(sys, "last_exc"):
|
||||||
|
print_exception(sys.last_exc, limit, file, chain)
|
||||||
|
else:
|
||||||
|
print_exception(sys.last_type, sys.last_value, sys.last_traceback,
|
||||||
|
limit, file, chain)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Printing and Extracting Stacks.
|
# Printing and Extracting Stacks.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add :data:`sys.last_exc` and deprecate :data:`sys.last_type`, :data:`sys.last_value`
|
||||||
|
and :data:`sys.last_traceback`,
|
||||||
|
which hold the same information in its legacy form.
|
|
@ -1304,7 +1304,7 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose)
|
||||||
{
|
{
|
||||||
// List of names to clear in sys
|
// List of names to clear in sys
|
||||||
static const char * const sys_deletes[] = {
|
static const char * const sys_deletes[] = {
|
||||||
"path", "argv", "ps1", "ps2",
|
"path", "argv", "ps1", "ps2", "last_exc",
|
||||||
"last_type", "last_value", "last_traceback",
|
"last_type", "last_value", "last_traceback",
|
||||||
"__interactivehook__",
|
"__interactivehook__",
|
||||||
// path_hooks and path_importer_cache are cleared
|
// path_hooks and path_importer_cache are cleared
|
||||||
|
|
|
@ -776,6 +776,10 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (set_sys_last_vars) {
|
if (set_sys_last_vars) {
|
||||||
|
if (_PySys_SetAttr(&_Py_ID(last_exc), exc) < 0) {
|
||||||
|
_PyErr_Clear(tstate);
|
||||||
|
}
|
||||||
|
/* Legacy version: */
|
||||||
if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) {
|
if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) {
|
||||||
_PyErr_Clear(tstate);
|
_PyErr_Clear(tstate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2670,11 +2670,13 @@ stderr -- standard error object; used for error messages\n\
|
||||||
By assigning other file objects (or objects that behave like files)\n\
|
By assigning other file objects (or objects that behave like files)\n\
|
||||||
to these, it is possible to redirect all of the interpreter's I/O.\n\
|
to these, it is possible to redirect all of the interpreter's I/O.\n\
|
||||||
\n\
|
\n\
|
||||||
|
last_exc - the last uncaught exception\n\
|
||||||
|
Only available in an interactive session after a\n\
|
||||||
|
traceback has been printed.\n\
|
||||||
last_type -- type of last uncaught exception\n\
|
last_type -- type of last uncaught exception\n\
|
||||||
last_value -- value of last uncaught exception\n\
|
last_value -- value of last uncaught exception\n\
|
||||||
last_traceback -- traceback of last uncaught exception\n\
|
last_traceback -- traceback of last uncaught exception\n\
|
||||||
These three are only available in an interactive session after a\n\
|
These three are the (deprecated) legacy representation of last_exc.\n\
|
||||||
traceback has been printed.\n\
|
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
/* concatenating string here */
|
/* concatenating string here */
|
||||||
|
|
Loading…
Reference in a new issue