Skip to content

Commit aa36f83

Browse files
authored
gh-118702: Implement vectorcall for BaseException (#118703)
* BaseException_vectorcall() now creates a tuple from 'args' array. * Creation an exception using BaseException_vectorcall() is now a single function call, rather than having to call BaseException_new() and then BaseException_init(). Calling BaseException_init() is inefficient since it overrides the 'args' attribute. * _PyErr_SetKeyError() now uses PyObject_CallOneArg() to create the KeyError instance to use BaseException_vectorcall().
1 parent ec9d12b commit aa36f83

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

Lib/test/test_exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,29 @@ def test_memory_error_in_subinterp(self):
18171817
rc, _, err = script_helper.assert_python_ok("-c", code)
18181818
self.assertIn(b'MemoryError', err)
18191819

1820+
def test_keyerror_context(self):
1821+
# Make sure that _PyErr_SetKeyError() chains exceptions
1822+
try:
1823+
err1 = None
1824+
err2 = None
1825+
try:
1826+
d = {}
1827+
try:
1828+
raise ValueError("bug")
1829+
except Exception as exc:
1830+
err1 = exc
1831+
d[1]
1832+
except Exception as exc:
1833+
err2 = exc
1834+
1835+
self.assertIsInstance(err1, ValueError)
1836+
self.assertIsInstance(err2, KeyError)
1837+
self.assertEqual(err2.__context__, err1)
1838+
finally:
1839+
# Break any potential reference cycle
1840+
exc1 = None
1841+
exc2 = None
1842+
18201843

18211844
class NameErrorTests(unittest.TestCase):
18221845
def test_name_error_has_name(self):

Objects/exceptions.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,40 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
7878
return 0;
7979
}
8080

81+
82+
static PyObject *
83+
BaseException_vectorcall(PyObject *type_obj, PyObject * const*args,
84+
size_t nargsf, PyObject *kwnames)
85+
{
86+
PyTypeObject *type = _PyType_CAST(type_obj);
87+
if (!_PyArg_NoKwnames(type->tp_name, kwnames)) {
88+
return NULL;
89+
}
90+
91+
PyBaseExceptionObject *self;
92+
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
93+
if (!self) {
94+
return NULL;
95+
}
96+
97+
// The dict is created on the fly in PyObject_GenericSetAttr()
98+
self->dict = NULL;
99+
self->notes = NULL;
100+
self->traceback = NULL;
101+
self->cause = NULL;
102+
self->context = NULL;
103+
self->suppress_context = 0;
104+
105+
self->args = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf));
106+
if (!self->args) {
107+
Py_DECREF(self);
108+
return NULL;
109+
}
110+
111+
return (PyObject *)self;
112+
}
113+
114+
81115
static int
82116
BaseException_clear(PyBaseExceptionObject *self)
83117
{
@@ -486,6 +520,7 @@ static PyTypeObject _PyExc_BaseException = {
486520
(initproc)BaseException_init, /* tp_init */
487521
0, /* tp_alloc */
488522
BaseException_new, /* tp_new */
523+
.tp_vectorcall = BaseException_vectorcall,
489524
};
490525
/* the CPython API expects exceptions to be (PyObject *) - both a hold-over
491526
from the previous implementation and also allowing Python objects to be used
@@ -3675,6 +3710,11 @@ _PyExc_InitTypes(PyInterpreterState *interp)
36753710
if (_PyStaticType_InitBuiltin(interp, exc) < 0) {
36763711
return -1;
36773712
}
3713+
if (exc->tp_new == BaseException_new
3714+
&& exc->tp_init == (initproc)BaseException_init)
3715+
{
3716+
exc->tp_vectorcall = BaseException_vectorcall;
3717+
}
36783718
}
36793719
return 0;
36803720
}

Python/errors.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,14 @@ void
257257
_PyErr_SetKeyError(PyObject *arg)
258258
{
259259
PyThreadState *tstate = _PyThreadState_GET();
260-
PyObject *tup = PyTuple_Pack(1, arg);
261-
if (!tup) {
260+
PyObject *exc = PyObject_CallOneArg(PyExc_KeyError, arg);
261+
if (!exc) {
262262
/* caller will expect error to be set anyway */
263263
return;
264264
}
265-
_PyErr_SetObject(tstate, PyExc_KeyError, tup);
266-
Py_DECREF(tup);
265+
266+
_PyErr_SetObject(tstate, (PyObject*)Py_TYPE(exc), exc);
267+
Py_DECREF(exc);
267268
}
268269

269270
void

0 commit comments

Comments
 (0)