changeset: 104611:7c3ec24f4582 parent: 104609:2119cb0beace parent: 104610:f7e1e39ccddd user: Serhiy Storchaka date: Fri Oct 21 17:15:20 2016 +0300 files: Misc/NEWS description: Issue #28214: Improved exception reporting for problematic __set_name__ attributes. diff -r 2119cb0beace -r 7c3ec24f4582 Lib/test/test_subclassinit.py --- a/Lib/test/test_subclassinit.py Fri Oct 21 17:10:42 2016 +0300 +++ b/Lib/test/test_subclassinit.py Fri Oct 21 17:15:20 2016 +0300 @@ -133,20 +133,32 @@ def test_set_name_error(self): class Descriptor: def __set_name__(self, owner, name): - raise RuntimeError + 1/0 + + with self.assertRaises(RuntimeError) as cm: + class NotGoingToWork: + attr = Descriptor() - with self.assertRaises(RuntimeError): - class A: - d = Descriptor() + exc = cm.exception + self.assertRegex(str(exc), r'\bNotGoingToWork\b') + self.assertRegex(str(exc), r'\battr\b') + self.assertRegex(str(exc), r'\bDescriptor\b') + self.assertIsInstance(exc.__cause__, ZeroDivisionError) def test_set_name_wrong(self): class Descriptor: def __set_name__(self): pass - with self.assertRaises(TypeError): - class A: - d = Descriptor() + with self.assertRaises(RuntimeError) as cm: + class NotGoingToWork: + attr = Descriptor() + + exc = cm.exception + self.assertRegex(str(exc), r'\bNotGoingToWork\b') + self.assertRegex(str(exc), r'\battr\b') + self.assertRegex(str(exc), r'\bDescriptor\b') + self.assertIsInstance(exc.__cause__, TypeError) def test_set_name_lookup(self): resolved = [] diff -r 2119cb0beace -r 7c3ec24f4582 Misc/NEWS --- a/Misc/NEWS Fri Oct 21 17:10:42 2016 +0300 +++ b/Misc/NEWS Fri Oct 21 17:15:20 2016 +0300 @@ -10,6 +10,9 @@ Core and Builtins ----------------- +- Issue #28214: Improved exception reporting for problematic __set_name__ + attributes. + - Issue #23782: Fixed possible memory leak in _PyTraceback_Add() and exception loss in PyTraceBack_Here(). diff -r 2119cb0beace -r 7c3ec24f4582 Objects/typeobject.c --- a/Objects/typeobject.c Fri Oct 21 17:10:42 2016 +0300 +++ b/Objects/typeobject.c Fri Oct 21 17:15:20 2016 +0300 @@ -7022,8 +7022,13 @@ if (set_name != NULL) { tmp = PyObject_CallFunctionObjArgs(set_name, type, key, NULL); Py_DECREF(set_name); - if (tmp == NULL) + if (tmp == NULL) { + _PyErr_FormatFromCause(PyExc_RuntimeError, + "Error calling __set_name__ on '%.100s' instance %R " + "in '%.100s'", + value->ob_type->tp_name, key, type->tp_name); return -1; + } else Py_DECREF(tmp); }