Skip to content

gh-118702: Implement vectorcall for BaseException #118703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,29 @@ def test_memory_error_in_subinterp(self):
rc, _, err = script_helper.assert_python_ok("-c", code)
self.assertIn(b'MemoryError', err)

def test_keyerror_context(self):
# Make sure that _PyErr_SetKeyError() chains exceptions
try:
err1 = None
err2 = None
try:
d = {}
try:
raise ValueError("bug")
except Exception as exc:
err1 = exc
d[1]
except Exception as exc:
err2 = exc

self.assertIsInstance(err1, ValueError)
self.assertIsInstance(err2, KeyError)
self.assertEqual(err2.__context__, err1)
finally:
# Break any potential reference cycle
exc1 = None
exc2 = None


class NameErrorTests(unittest.TestCase):
def test_name_error_has_name(self):
Expand Down
40 changes: 40 additions & 0 deletions Objects/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,40 @@ BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds)
return 0;
}


static PyObject *
BaseException_vectorcall(PyObject *type_obj, PyObject * const*args,
size_t nargsf, PyObject *kwnames)
{
PyTypeObject *type = _PyType_CAST(type_obj);
if (!_PyArg_NoKwnames(type->tp_name, kwnames)) {
return NULL;
}

PyBaseExceptionObject *self;
self = (PyBaseExceptionObject *)type->tp_alloc(type, 0);
if (!self) {
return NULL;
}

// The dict is created on the fly in PyObject_GenericSetAttr()
self->dict = NULL;
self->notes = NULL;
self->traceback = NULL;
self->cause = NULL;
self->context = NULL;
self->suppress_context = 0;

self->args = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf));
if (!self->args) {
Py_DECREF(self);
return NULL;
}

return (PyObject *)self;
}


static int
BaseException_clear(PyBaseExceptionObject *self)
{
Expand Down Expand Up @@ -486,6 +520,7 @@ static PyTypeObject _PyExc_BaseException = {
(initproc)BaseException_init, /* tp_init */
0, /* tp_alloc */
BaseException_new, /* tp_new */
.tp_vectorcall = BaseException_vectorcall,
};
/* the CPython API expects exceptions to be (PyObject *) - both a hold-over
from the previous implementation and also allowing Python objects to be used
Expand Down Expand Up @@ -3675,6 +3710,11 @@ _PyExc_InitTypes(PyInterpreterState *interp)
if (_PyStaticType_InitBuiltin(interp, exc) < 0) {
return -1;
}
if (exc->tp_new == BaseException_new
&& exc->tp_init == (initproc)BaseException_init)
{
exc->tp_vectorcall = BaseException_vectorcall;
}
}
return 0;
}
Expand Down
9 changes: 5 additions & 4 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,14 @@ void
_PyErr_SetKeyError(PyObject *arg)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *tup = PyTuple_Pack(1, arg);
if (!tup) {
PyObject *exc = PyObject_CallOneArg(PyExc_KeyError, arg);
if (!exc) {
/* caller will expect error to be set anyway */
return;
}
_PyErr_SetObject(tstate, PyExc_KeyError, tup);
Py_DECREF(tup);

_PyErr_SetObject(tstate, (PyObject*)Py_TYPE(exc), exc);
Py_DECREF(exc);
}

void
Expand Down