Skip to content

Commit b4b6342

Browse files
iritkatrielErlend Egeberg Aasland
andauthored
bpo-45083: Include the exception class qualname when formatting an exception (pythonGH-28119)
Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
1 parent a1e15a7 commit b4b6342

File tree

5 files changed

+65
-35
lines changed

5 files changed

+65
-35
lines changed

Lib/test/test_sys.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,20 @@ def __del__(self):
10701070
self.assertIn("del is broken", report)
10711071
self.assertTrue(report.endswith("\n"))
10721072

1073+
def test_original_unraisablehook_exception_qualname(self):
1074+
class A:
1075+
class B:
1076+
class X(Exception):
1077+
pass
1078+
1079+
with test.support.captured_stderr() as stderr, \
1080+
test.support.swap_attr(sys, 'unraisablehook',
1081+
sys.__unraisablehook__):
1082+
expected = self.write_unraisable_exc(
1083+
A.B.X(), "msg", "obj");
1084+
report = stderr.getvalue()
1085+
testName = 'test_original_unraisablehook_exception_qualname'
1086+
self.assertIn(f"{testName}.<locals>.A.B.X", report)
10731087

10741088
def test_original_unraisablehook_wrong_type(self):
10751089
exc = ValueError(42)

Lib/test/test_traceback.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,19 @@ def test_syntax_error_various_offsets(self):
11711171
exp = "\n".join(expected)
11721172
self.assertEqual(exp, err)
11731173

1174+
def test_format_exception_only_qualname(self):
1175+
class A:
1176+
class B:
1177+
class X(Exception):
1178+
def __str__(self):
1179+
return "I am X"
1180+
pass
1181+
err = self.get_report(A.B.X())
1182+
str_value = 'I am X'
1183+
str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__])
1184+
exp = "%s: %s\n" % (str_name, str_value)
1185+
self.assertEqual(exp, err)
1186+
11741187

11751188
class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
11761189
#
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When the interpreter renders an exception, its name now has a complete qualname. Previously only the class name was concatenated to the module name, which sometimes resulted in an incorrect full name being displayed.
2+
3+
(This issue impacted only the C code exception rendering, the :mod:`traceback` module was using qualname already).

Python/errors.c

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,46 +1287,45 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
12871287
}
12881288

12891289
assert(PyExceptionClass_Check(exc_type));
1290-
const char *className = PyExceptionClass_Name(exc_type);
1291-
if (className != NULL) {
1292-
const char *dot = strrchr(className, '.');
1293-
if (dot != NULL) {
1294-
className = dot+1;
1295-
}
1296-
}
12971290

1298-
PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__);
1299-
if (moduleName == NULL || !PyUnicode_Check(moduleName)) {
1300-
Py_XDECREF(moduleName);
1291+
PyObject *modulename = _PyObject_GetAttrId(exc_type, &PyId___module__);
1292+
if (modulename == NULL || !PyUnicode_Check(modulename)) {
1293+
Py_XDECREF(modulename);
13011294
_PyErr_Clear(tstate);
13021295
if (PyFile_WriteString("<unknown>", file) < 0) {
13031296
return -1;
13041297
}
13051298
}
13061299
else {
1307-
if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) {
1308-
if (PyFile_WriteObject(moduleName, file, Py_PRINT_RAW) < 0) {
1309-
Py_DECREF(moduleName);
1300+
if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins)) {
1301+
if (PyFile_WriteObject(modulename, file, Py_PRINT_RAW) < 0) {
1302+
Py_DECREF(modulename);
13101303
return -1;
13111304
}
1312-
Py_DECREF(moduleName);
1305+
Py_DECREF(modulename);
13131306
if (PyFile_WriteString(".", file) < 0) {
13141307
return -1;
13151308
}
13161309
}
13171310
else {
1318-
Py_DECREF(moduleName);
1311+
Py_DECREF(modulename);
13191312
}
13201313
}
1321-
if (className == NULL) {
1314+
1315+
PyObject *qualname = PyType_GetQualName((PyTypeObject *)exc_type);
1316+
if (qualname == NULL || !PyUnicode_Check(qualname)) {
1317+
Py_XDECREF(qualname);
1318+
_PyErr_Clear(tstate);
13221319
if (PyFile_WriteString("<unknown>", file) < 0) {
13231320
return -1;
13241321
}
13251322
}
13261323
else {
1327-
if (PyFile_WriteString(className, file) < 0) {
1324+
if (PyFile_WriteObject(qualname, file, Py_PRINT_RAW) < 0) {
1325+
Py_DECREF(qualname);
13281326
return -1;
13291327
}
1328+
Py_DECREF(qualname);
13301329
}
13311330

13321331
if (exc_value && exc_value != Py_None) {

Python/pythonrun.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -961,36 +961,37 @@ print_exception(PyObject *f, PyObject *value)
961961
/* Don't do anything else */
962962
}
963963
else {
964-
PyObject* moduleName;
965-
const char *className;
964+
PyObject* modulename;
965+
966966
_Py_IDENTIFIER(__module__);
967967
assert(PyExceptionClass_Check(type));
968-
className = PyExceptionClass_Name(type);
969-
if (className != NULL) {
970-
const char *dot = strrchr(className, '.');
971-
if (dot != NULL)
972-
className = dot+1;
973-
}
974968

975-
moduleName = _PyObject_GetAttrId(type, &PyId___module__);
976-
if (moduleName == NULL || !PyUnicode_Check(moduleName))
969+
modulename = _PyObject_GetAttrId(type, &PyId___module__);
970+
if (modulename == NULL || !PyUnicode_Check(modulename))
977971
{
978-
Py_XDECREF(moduleName);
972+
Py_XDECREF(modulename);
973+
PyErr_Clear();
979974
err = PyFile_WriteString("<unknown>", f);
980975
}
981976
else {
982-
if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins))
977+
if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins))
983978
{
984-
err = PyFile_WriteObject(moduleName, f, Py_PRINT_RAW);
979+
err = PyFile_WriteObject(modulename, f, Py_PRINT_RAW);
985980
err += PyFile_WriteString(".", f);
986981
}
987-
Py_DECREF(moduleName);
982+
Py_DECREF(modulename);
988983
}
989984
if (err == 0) {
990-
if (className == NULL)
991-
err = PyFile_WriteString("<unknown>", f);
992-
else
993-
err = PyFile_WriteString(className, f);
985+
PyObject* qualname = PyType_GetQualName((PyTypeObject *)type);
986+
if (qualname == NULL || !PyUnicode_Check(qualname)) {
987+
Py_XDECREF(qualname);
988+
PyErr_Clear();
989+
err = PyFile_WriteString("<unknown>", f);
990+
}
991+
else {
992+
err = PyFile_WriteObject(qualname, f, Py_PRINT_RAW);
993+
Py_DECREF(qualname);
994+
}
994995
}
995996
}
996997
if (err == 0 && (value != Py_None)) {

0 commit comments

Comments
 (0)