Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Remove @deleter from setters which only reject the deletion
They raised TypeError for deletion, and the generated code now raises
AttributeError.
  • Loading branch information
serhiy-storchaka committed Aug 20, 2026
commit 5b9d005d240a133495d9be5664b4ad61499bc8ed
4 changes: 2 additions & 2 deletions Lib/test/test_ctypes/test_delattr.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ class X(Structure):

class TestCase(unittest.TestCase):
def test_simple(self):
with self.assertRaises(TypeError):
with self.assertRaises(AttributeError):
del c_int(42).value

def test_chararray(self):
chararray = (c_char * 5)()
with self.assertRaises(TypeError):
with self.assertRaises(AttributeError):
del chararray.value

def test_struct(self):
Expand Down
12 changes: 6 additions & 6 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,17 +683,17 @@ def test_invalid_setattr(self):
self.assertRaisesRegex(TE, msg, setattr, exc, '__context__', 1)

def test_invalid_delattr(self):
TE = TypeError
AE = AttributeError
try:
raise IndexError(4)
except Exception as e:
exc = e

msg = "may not be deleted"
self.assertRaisesRegex(TE, msg, delattr, exc, 'args')
self.assertRaisesRegex(TE, msg, delattr, exc, '__traceback__')
self.assertRaisesRegex(TE, msg, delattr, exc, '__cause__')
self.assertRaisesRegex(TE, msg, delattr, exc, '__context__')
msg = "cannot be deleted"
self.assertRaisesRegex(AE, msg, delattr, exc, 'args')
self.assertRaisesRegex(AE, msg, delattr, exc, '__traceback__')
self.assertRaisesRegex(AE, msg, delattr, exc, '__cause__')
self.assertRaisesRegex(AE, msg, delattr, exc, '__context__')

def testNoneClearsTracebackAttr(self):
try:
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_funcattrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def not_generic(): pass
for func in (not_generic, lambda_):
with self.subTest(func=func):
self.assertEqual(func.__type_params__, ())
with self.assertRaises(TypeError):
with self.assertRaises(AttributeError):
del func.__type_params__
with self.assertRaises(TypeError):
func.__type_params__ = 42
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_raise.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def test_attrs(self):
self.assertIs(tb.tb_next.tb_next, None)

# Invalid assignments
with self.assertRaises(TypeError):
with self.assertRaises(AttributeError):
del tb.tb_next

with self.assertRaises(TypeError):
Expand Down
20 changes: 3 additions & 17 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1533,22 +1533,15 @@ _ctypes_PyCArrayType_Type_value_get_impl(CDataObject *self)
/*[clinic input]
@critical_section
@setter
@deleter
_ctypes.PyCArrayType_Type.value
value: object(subclass_of='&PyBytes_Type', type='PyBytesObject *') = NULL
value: object(subclass_of='&PyBytes_Type', type='PyBytesObject *')
[clinic start generated code]*/

static int
_ctypes_PyCArrayType_Type_value_set_impl(CDataObject *self,
PyBytesObject *value)
/*[clinic end generated code: output=21cbd436230dc33e input=d35228bbfb2f5228]*/
/*[clinic end generated code: output=21cbd436230dc33e input=47d40501c4ecae23]*/
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
"can't delete attribute");
return -1;
}

Py_ssize_t size = PyBytes_GET_SIZE(value);
if (size > self->b_size) {
PyErr_SetString(PyExc_ValueError,
Expand Down Expand Up @@ -5388,22 +5381,15 @@ class _ctypes.Simple "CDataObject *" "clinic_state()->Simple_Type"
/*[clinic input]
@critical_section
@setter
@deleter
_ctypes.Simple.value
[clinic start generated code]*/

static int
_ctypes_Simple_value_set_impl(CDataObject *self, PyObject *value)
/*[clinic end generated code: output=f267186118939863 input=4e6c1143d17c2c3f]*/
/*[clinic end generated code: output=f267186118939863 input=977af9dc9e71e857]*/
{
PyObject *result;

if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
"can't delete attribute");
return -1;
}

ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
StgInfo *info;
if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) {
Expand Down
30 changes: 19 additions & 11 deletions Modules/_ctypes/clinic/_ctypes.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 29 additions & 13 deletions Objects/clinic/exceptions.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 19 additions & 11 deletions Objects/clinic/funcobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 6 additions & 24 deletions Objects/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,19 +346,14 @@ BaseException_args_get_impl(PyBaseExceptionObject *self)
/*[clinic input]
@critical_section
@setter
@deleter
BaseException.args
[clinic start generated code]*/

static int
BaseException_args_set_impl(PyBaseExceptionObject *self, PyObject *value)
/*[clinic end generated code: output=331137e11d8f9e80 input=177ad350c8b45219]*/
/*[clinic end generated code: output=331137e11d8f9e80 input=2400047ea5970a84]*/
{
PyObject *seq;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "args may not be deleted");
return -1;
}
seq = PySequence_Tuple(value);
if (!seq)
return -1;
Expand Down Expand Up @@ -386,19 +381,14 @@ BaseException___traceback___get_impl(PyBaseExceptionObject *self)
/*[clinic input]
@critical_section
@setter
@deleter
BaseException.__traceback__
[clinic start generated code]*/

static int
BaseException___traceback___set_impl(PyBaseExceptionObject *self,
PyObject *value)
/*[clinic end generated code: output=a82c86d9f29f48f0 input=53a1df586023d786]*/
/*[clinic end generated code: output=a82c86d9f29f48f0 input=12676035676badad]*/
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted");
return -1;
}
if (PyTraceBack_Check(value)) {
Py_XSETREF(self->traceback, Py_NewRef(value));
}
Expand Down Expand Up @@ -432,19 +422,15 @@ BaseException___context___get_impl(PyBaseExceptionObject *self)
/*[clinic input]
@critical_section
@setter
@deleter
BaseException.__context__
[clinic start generated code]*/

static int
BaseException___context___set_impl(PyBaseExceptionObject *self,
PyObject *value)
/*[clinic end generated code: output=b4cb52dcca1da3bd input=fe79e7c0a0854004]*/
/*[clinic end generated code: output=b4cb52dcca1da3bd input=c0971adf47fa1858]*/
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted");
return -1;
} else if (value == Py_None) {
if (value == Py_None) {
value = NULL;
} else if (!PyExceptionInstance_Check(value)) {
PyErr_SetString(PyExc_TypeError, "exception context must be None "
Expand Down Expand Up @@ -476,19 +462,15 @@ BaseException___cause___get_impl(PyBaseExceptionObject *self)
/*[clinic input]
@critical_section
@setter
@deleter
BaseException.__cause__
[clinic start generated code]*/

static int
BaseException___cause___set_impl(PyBaseExceptionObject *self,
PyObject *value)
/*[clinic end generated code: output=6161315398aaf541 input=3fdd9a0d1674abc9]*/
/*[clinic end generated code: output=6161315398aaf541 input=e1b403c0bde3f62a]*/
{
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
return -1;
} else if (value == Py_None) {
if (value == Py_None) {
value = NULL;
} else if (!PyExceptionInstance_Check(value)) {
PyErr_SetString(PyExc_TypeError, "exception cause must be None "
Expand Down
Loading