From c2ce88eb6d6664e8d77e0ee38a955f44af89eb9a Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 2 Sep 2021 00:45:25 +0100 Subject: [PATCH 1/6] bpo-45083: Include the exception class qualname when formatting an exception --- Lib/test/test_sys.py | 14 ++++++++++++++ Lib/test/test_traceback.py | 13 +++++++++++++ Python/errors.c | 13 +++++++------ Python/pythonrun.c | 18 ++++++++++++------ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index dba4928ec261ac..12305ca95d0a0d 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1070,6 +1070,20 @@ def __del__(self): self.assertIn("del is broken", report) self.assertTrue(report.endswith("\n")) + def test_original_unraisablehook_exception_qualname(self): + class A: + class B: + class X(Exception): + pass + + with test.support.captured_stderr() as stderr, \ + test.support.swap_attr(sys, 'unraisablehook', + sys.__unraisablehook__): + expected = self.write_unraisable_exc( + A.B.X(), "msg", "obj"); + report = stderr.getvalue() + testName = 'test_original_unraisablehook_exception_qualname' + self.assertIn(f"{testName}..A.B.X", report) def test_original_unraisablehook_wrong_type(self): exc = ValueError(42) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index d1967aabb29a1c..ee2896c5f4867c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1171,6 +1171,19 @@ def test_syntax_error_various_offsets(self): exp = "\n".join(expected) self.assertEqual(exp, err) + def test_format_exception_only_qualname(self): + class A: + class B: + class X(Exception): + def __str__(self): + return "I am X" + pass + err = self.get_report(A.B.X()) + str_value = 'I am X' + str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__]) + exp = "%s: %s\n" % (str_name, str_value) + self.assertEqual(exp, err) + class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # diff --git a/Python/errors.c b/Python/errors.c index ae1cde690eafa5..6ac3d327896315 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1287,12 +1287,13 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, } assert(PyExceptionClass_Check(exc_type)); - const char *className = PyExceptionClass_Name(exc_type); - if (className != NULL) { - const char *dot = strrchr(className, '.'); - if (dot != NULL) { - className = dot+1; - } + PyObject *qualName = PyType_GetQualName((PyTypeObject *)exc_type); + if (!qualName) { + return -1; + } + const char *className = PyUnicode_AsUTF8(qualName); + if (!className) { + return -1; } PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__); diff --git a/Python/pythonrun.c b/Python/pythonrun.c index f00e3eb0de803f..4df5e382881163 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -962,14 +962,20 @@ print_exception(PyObject *f, PyObject *value) } else { PyObject* moduleName; - const char *className; + PyObject* qualName; + const char* className = NULL; + _Py_IDENTIFIER(__module__); assert(PyExceptionClass_Check(type)); - className = PyExceptionClass_Name(type); - if (className != NULL) { - const char *dot = strrchr(className, '.'); - if (dot != NULL) - className = dot+1; + + qualName = PyType_GetQualName((PyTypeObject *)type); + if (!qualName) { + PyErr_Clear(); + } else { + className = PyUnicode_AsUTF8(qualName); + if (!className) { + PyErr_Clear(); + } } moduleName = _PyObject_GetAttrId(type, &PyId___module__); From 39f24c69148c11fa37beefc00b0b5982bfe4aaef Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 1 Sep 2021 23:55:50 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2021-09-01-23-55-49.bpo-45083.cLi9G3.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-09-01-23-55-49.bpo-45083.cLi9G3.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-09-01-23-55-49.bpo-45083.cLi9G3.rst b/Misc/NEWS.d/next/Core and Builtins/2021-09-01-23-55-49.bpo-45083.cLi9G3.rst new file mode 100644 index 00000000000000..7bfd87b9420593 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-09-01-23-55-49.bpo-45083.cLi9G3.rst @@ -0,0 +1,3 @@ +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. + +(This issue impacted only the C code exception rendering, the :mod:`traceback` module was using qualname already). \ No newline at end of file From b8c09cebaf4bf952fc05b7be0de829d602a17444 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:36:13 +0100 Subject: [PATCH 3/6] == NULL Co-authored-by: Erlend Egeberg Aasland --- Python/errors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/errors.c b/Python/errors.c index 6ac3d327896315..e390187215dcc9 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1288,7 +1288,7 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, assert(PyExceptionClass_Check(exc_type)); PyObject *qualName = PyType_GetQualName((PyTypeObject *)exc_type); - if (!qualName) { + if (qualName == NULL) { return -1; } const char *className = PyUnicode_AsUTF8(qualName); From 0da8f5b906cc0072a5f37be37311abed9a3ad80f Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:36:41 +0100 Subject: [PATCH 4/6] newline before else Co-authored-by: Erlend Egeberg Aasland --- Python/pythonrun.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 4df5e382881163..2c301bd33a27f7 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -971,7 +971,8 @@ print_exception(PyObject *f, PyObject *value) qualName = PyType_GetQualName((PyTypeObject *)type); if (!qualName) { PyErr_Clear(); - } else { + } + else { className = PyUnicode_AsUTF8(qualName); if (!className) { PyErr_Clear(); From 9781ce50d26fff86a9a0686629aa2e600e481465 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:38:00 +0100 Subject: [PATCH 5/6] move * on pointer Co-authored-by: Erlend Egeberg Aasland --- Python/pythonrun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 2c301bd33a27f7..b4e34fb504960e 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -963,7 +963,7 @@ print_exception(PyObject *f, PyObject *value) else { PyObject* moduleName; PyObject* qualName; - const char* className = NULL; + const char *className = NULL; _Py_IDENTIFIER(__module__); assert(PyExceptionClass_Check(type)); From f863ff054a9fe8608b216cb538d73cc0ed0c96f7 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 2 Sep 2021 14:37:53 +0100 Subject: [PATCH 6/6] simplify code and fix qualname refleak --- Python/errors.c | 34 ++++++++++++++++------------------ Python/pythonrun.c | 42 ++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/Python/errors.c b/Python/errors.c index e390187215dcc9..15ca21b68400f2 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1287,47 +1287,45 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type, } assert(PyExceptionClass_Check(exc_type)); - PyObject *qualName = PyType_GetQualName((PyTypeObject *)exc_type); - if (qualName == NULL) { - return -1; - } - const char *className = PyUnicode_AsUTF8(qualName); - if (!className) { - return -1; - } - PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__); - if (moduleName == NULL || !PyUnicode_Check(moduleName)) { - Py_XDECREF(moduleName); + PyObject *modulename = _PyObject_GetAttrId(exc_type, &PyId___module__); + if (modulename == NULL || !PyUnicode_Check(modulename)) { + Py_XDECREF(modulename); _PyErr_Clear(tstate); if (PyFile_WriteString("", file) < 0) { return -1; } } else { - if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) { - if (PyFile_WriteObject(moduleName, file, Py_PRINT_RAW) < 0) { - Py_DECREF(moduleName); + if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins)) { + if (PyFile_WriteObject(modulename, file, Py_PRINT_RAW) < 0) { + Py_DECREF(modulename); return -1; } - Py_DECREF(moduleName); + Py_DECREF(modulename); if (PyFile_WriteString(".", file) < 0) { return -1; } } else { - Py_DECREF(moduleName); + Py_DECREF(modulename); } } - if (className == NULL) { + + PyObject *qualname = PyType_GetQualName((PyTypeObject *)exc_type); + if (qualname == NULL || !PyUnicode_Check(qualname)) { + Py_XDECREF(qualname); + _PyErr_Clear(tstate); if (PyFile_WriteString("", file) < 0) { return -1; } } else { - if (PyFile_WriteString(className, file) < 0) { + if (PyFile_WriteObject(qualname, file, Py_PRINT_RAW) < 0) { + Py_DECREF(qualname); return -1; } + Py_DECREF(qualname); } if (exc_value && exc_value != Py_None) { diff --git a/Python/pythonrun.c b/Python/pythonrun.c index b4e34fb504960e..3d07f43b5256d1 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -961,43 +961,37 @@ print_exception(PyObject *f, PyObject *value) /* Don't do anything else */ } else { - PyObject* moduleName; - PyObject* qualName; - const char *className = NULL; + PyObject* modulename; _Py_IDENTIFIER(__module__); assert(PyExceptionClass_Check(type)); - qualName = PyType_GetQualName((PyTypeObject *)type); - if (!qualName) { - PyErr_Clear(); - } - else { - className = PyUnicode_AsUTF8(qualName); - if (!className) { - PyErr_Clear(); - } - } - - moduleName = _PyObject_GetAttrId(type, &PyId___module__); - if (moduleName == NULL || !PyUnicode_Check(moduleName)) + modulename = _PyObject_GetAttrId(type, &PyId___module__); + if (modulename == NULL || !PyUnicode_Check(modulename)) { - Py_XDECREF(moduleName); + Py_XDECREF(modulename); + PyErr_Clear(); err = PyFile_WriteString("", f); } else { - if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) + if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins)) { - err = PyFile_WriteObject(moduleName, f, Py_PRINT_RAW); + err = PyFile_WriteObject(modulename, f, Py_PRINT_RAW); err += PyFile_WriteString(".", f); } - Py_DECREF(moduleName); + Py_DECREF(modulename); } if (err == 0) { - if (className == NULL) - err = PyFile_WriteString("", f); - else - err = PyFile_WriteString(className, f); + PyObject* qualname = PyType_GetQualName((PyTypeObject *)type); + if (qualname == NULL || !PyUnicode_Check(qualname)) { + Py_XDECREF(qualname); + PyErr_Clear(); + err = PyFile_WriteString("", f); + } + else { + err = PyFile_WriteObject(qualname, f, Py_PRINT_RAW); + Py_DECREF(qualname); + } } } if (err == 0 && (value != Py_None)) {