Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 55c99d9

Browse filesBrowse files
authored
pythongh-77757: replace exception wrapping by PEP-678 notes in typeobject's __set_name__ (python#103402)
1 parent e071f00 commit 55c99d9
Copy full SHA for 55c99d9

File tree

8 files changed

+55
-41
lines changed
Filter options

8 files changed

+55
-41
lines changed

‎Doc/whatsnew/3.12.rst

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.12.rst
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ Other Language Changes
192192
* :class:`slice` objects are now hashable, allowing them to be used as dict keys and
193193
set items. (Contributed by Will Bradshaw and Furkan Onder in :gh:`101264`.)
194194

195+
* Exceptions raised in a typeobject's ``__set_name__`` method are no longer
196+
wrapped by a :exc:`RuntimeError`. Context information is added to the
197+
exception as a :pep:`678` note. (Contributed by Irit Katriel in :gh:`77757`.)
198+
195199
New Modules
196200
===========
197201

‎Include/internal/pycore_pyerrors.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_pyerrors.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ extern PyObject* _Py_Offer_Suggestions(PyObject* exception);
109109
PyAPI_FUNC(Py_ssize_t) _Py_UTF8_Edit_Cost(PyObject *str_a, PyObject *str_b,
110110
Py_ssize_t max_cost);
111111

112+
void _PyErr_FormatNote(const char *format, ...);
113+
112114
#ifdef __cplusplus
113115
}
114116
#endif

‎Lib/test/test_functools.py

Copy file name to clipboardExpand all lines: Lib/test/test_functools.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2980,7 +2980,7 @@ class MyClass(metaclass=MyMeta):
29802980

29812981
def test_reuse_different_names(self):
29822982
"""Disallow this case because decorated function a would not be cached."""
2983-
with self.assertRaises(RuntimeError) as ctx:
2983+
with self.assertRaises(TypeError) as ctx:
29842984
class ReusedCachedProperty:
29852985
@py_functools.cached_property
29862986
def a(self):
@@ -2989,7 +2989,7 @@ def a(self):
29892989
b = a
29902990

29912991
self.assertEqual(
2992-
str(ctx.exception.__context__),
2992+
str(ctx.exception),
29932993
str(TypeError("Cannot assign the same cached_property to two different names ('a' and 'b')."))
29942994
)
29952995

‎Lib/test/test_subclassinit.py

Copy file name to clipboardExpand all lines: Lib/test/test_subclassinit.py
+10-12Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,28 @@ class Descriptor:
134134
def __set_name__(self, owner, name):
135135
1/0
136136

137-
with self.assertRaises(RuntimeError) as cm:
137+
with self.assertRaises(ZeroDivisionError) as cm:
138138
class NotGoingToWork:
139139
attr = Descriptor()
140140

141-
exc = cm.exception
142-
self.assertRegex(str(exc), r'\bNotGoingToWork\b')
143-
self.assertRegex(str(exc), r'\battr\b')
144-
self.assertRegex(str(exc), r'\bDescriptor\b')
145-
self.assertIsInstance(exc.__cause__, ZeroDivisionError)
141+
notes = cm.exception.__notes__
142+
self.assertRegex(str(notes), r'\bNotGoingToWork\b')
143+
self.assertRegex(str(notes), r'\battr\b')
144+
self.assertRegex(str(notes), r'\bDescriptor\b')
146145

147146
def test_set_name_wrong(self):
148147
class Descriptor:
149148
def __set_name__(self):
150149
pass
151150

152-
with self.assertRaises(RuntimeError) as cm:
151+
with self.assertRaises(TypeError) as cm:
153152
class NotGoingToWork:
154153
attr = Descriptor()
155154

156-
exc = cm.exception
157-
self.assertRegex(str(exc), r'\bNotGoingToWork\b')
158-
self.assertRegex(str(exc), r'\battr\b')
159-
self.assertRegex(str(exc), r'\bDescriptor\b')
160-
self.assertIsInstance(exc.__cause__, TypeError)
155+
notes = cm.exception.__notes__
156+
self.assertRegex(str(notes), r'\bNotGoingToWork\b')
157+
self.assertRegex(str(notes), r'\battr\b')
158+
self.assertRegex(str(notes), r'\bDescriptor\b')
161159

162160
def test_set_name_lookup(self):
163161
resolved = []
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Exceptions raised in a typeobject's ``__set_name__`` method are no longer
2+
wrapped by a :exc:`RuntimeError`. Context information is added to the
3+
exception as a :pep:`678` note.

‎Objects/typeobject.c

Copy file name to clipboardExpand all lines: Objects/typeobject.c
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9137,13 +9137,15 @@ type_new_set_names(PyTypeObject *type)
91379137
Py_DECREF(set_name);
91389138

91399139
if (res == NULL) {
9140-
_PyErr_FormatFromCause(PyExc_RuntimeError,
9140+
_PyErr_FormatNote(
91419141
"Error calling __set_name__ on '%.100s' instance %R "
91429142
"in '%.100s'",
91439143
Py_TYPE(value)->tp_name, key, type->tp_name);
91449144
goto error;
91459145
}
9146-
Py_DECREF(res);
9146+
else {
9147+
Py_DECREF(res);
9148+
}
91479149
}
91489150

91499151
Py_DECREF(names_to_set);

‎Python/codecs.c

Copy file name to clipboardExpand all lines: Python/codecs.c
+3-25Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Copyright (c) Corporation for National Research Initiatives.
1111
#include "Python.h"
1212
#include "pycore_call.h" // _PyObject_CallNoArgs()
1313
#include "pycore_interp.h" // PyInterpreterState.codec_search_path
14+
#include "pycore_pyerrors.h" // _PyErr_FormatNote()
1415
#include "pycore_pystate.h" // _PyInterpreterState_GET()
1516
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
1617
#include <ctype.h>
@@ -382,29 +383,6 @@ PyObject *PyCodec_StreamWriter(const char *encoding,
382383
return codec_getstreamcodec(encoding, stream, errors, 3);
383384
}
384385

385-
static void
386-
add_note_to_codec_error(const char *operation,
387-
const char *encoding)
388-
{
389-
PyObject *exc = PyErr_GetRaisedException();
390-
if (exc == NULL) {
391-
return;
392-
}
393-
PyObject *note = PyUnicode_FromFormat("%s with '%s' codec failed",
394-
operation, encoding);
395-
if (note == NULL) {
396-
_PyErr_ChainExceptions1(exc);
397-
return;
398-
}
399-
int res = _PyException_AddNote(exc, note);
400-
Py_DECREF(note);
401-
if (res < 0) {
402-
_PyErr_ChainExceptions1(exc);
403-
return;
404-
}
405-
PyErr_SetRaisedException(exc);
406-
}
407-
408386
/* Encode an object (e.g. a Unicode object) using the given encoding
409387
and return the resulting encoded object (usually a Python string).
410388
@@ -425,7 +403,7 @@ _PyCodec_EncodeInternal(PyObject *object,
425403

426404
result = PyObject_Call(encoder, args, NULL);
427405
if (result == NULL) {
428-
add_note_to_codec_error("encoding", encoding);
406+
_PyErr_FormatNote("%s with '%s' codec failed", "encoding", encoding);
429407
goto onError;
430408
}
431409

@@ -470,7 +448,7 @@ _PyCodec_DecodeInternal(PyObject *object,
470448

471449
result = PyObject_Call(decoder, args, NULL);
472450
if (result == NULL) {
473-
add_note_to_codec_error("decoding", encoding);
451+
_PyErr_FormatNote("%s with '%s' codec failed", "decoding", encoding);
474452
goto onError;
475453
}
476454
if (!PyTuple_Check(result) ||

‎Python/errors.c

Copy file name to clipboardExpand all lines: Python/errors.c
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,33 @@ PyErr_Format(PyObject *exception, const char *format, ...)
12001200
}
12011201

12021202

1203+
/* Adds a note to the current exception (if any) */
1204+
void
1205+
_PyErr_FormatNote(const char *format, ...)
1206+
{
1207+
PyObject *exc = PyErr_GetRaisedException();
1208+
if (exc == NULL) {
1209+
return;
1210+
}
1211+
va_list vargs;
1212+
va_start(vargs, format);
1213+
PyObject *note = PyUnicode_FromFormatV(format, vargs);
1214+
va_end(vargs);
1215+
if (note == NULL) {
1216+
goto error;
1217+
}
1218+
int res = _PyException_AddNote(exc, note);
1219+
Py_DECREF(note);
1220+
if (res < 0) {
1221+
goto error;
1222+
}
1223+
PyErr_SetRaisedException(exc);
1224+
return;
1225+
error:
1226+
_PyErr_ChainExceptions1(exc);
1227+
}
1228+
1229+
12031230
PyObject *
12041231
PyErr_NewException(const char *name, PyObject *base, PyObject *dict)
12051232
{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.