From 66074b5cd9b5980113c5b74d29c3a8d786661bd1 Mon Sep 17 00:00:00 2001 From: oren mn Date: Tue, 28 Feb 2017 18:18:38 +0200 Subject: [PATCH 1/5] init commit, with initial tests for from_param and fields __set__ and __get__, and some additions to from_buffer and from_buffer_copy --- Lib/ctypes/test/test_frombuffer.py | 9 +++++++++ Lib/ctypes/test/test_parameters.py | 10 ++++++++++ Lib/ctypes/test/test_struct_fields.py | 24 ++++++++++++++++++++++++ Modules/_ctypes/_ctypes.c | 1 + 4 files changed, 44 insertions(+) diff --git a/Lib/ctypes/test/test_frombuffer.py b/Lib/ctypes/test/test_frombuffer.py index 7ab38f1b031f1f..55c244356b30d0 100644 --- a/Lib/ctypes/test/test_frombuffer.py +++ b/Lib/ctypes/test/test_frombuffer.py @@ -121,12 +121,21 @@ def test_from_buffer_copy_with_offset(self): (c_int * 1).from_buffer_copy(a, 16 * sizeof(c_int)) def test_abstract(self): + from ctypes import _Pointer, _SimpleCData, _CFuncPtr + self.assertRaises(TypeError, Array.from_buffer, bytearray(10)) self.assertRaises(TypeError, Structure.from_buffer, bytearray(10)) self.assertRaises(TypeError, Union.from_buffer, bytearray(10)) + self.assertRaises(TypeError, _CFuncPtr.from_buffer, bytearray(10)) + self.assertRaises(TypeError, _Pointer.from_buffer, bytearray(10)) + self.assertRaises(TypeError, _SimpleCData.from_buffer, bytearray(10)) + self.assertRaises(TypeError, Array.from_buffer_copy, b"123") self.assertRaises(TypeError, Structure.from_buffer_copy, b"123") self.assertRaises(TypeError, Union.from_buffer_copy, b"123") + self.assertRaises(TypeError, _CFuncPtr.from_buffer_copy, b"123") + self.assertRaises(TypeError, _Pointer.from_buffer_copy, b"123") + self.assertRaises(TypeError, _SimpleCData.from_buffer_copy, b"123") if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 363f58612dbdf9..48829cc3187ca5 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -169,6 +169,16 @@ def from_param(cls, obj): # ArgumentError: argument 1: ValueError: 99 self.assertRaises(ArgumentError, func, 99) + def test_abstract(self): + from ctypes import (Array, Structure, Union, _Pointer, + _SimpleCData, _CFuncPtr) + + self.assertRaises(TypeError, Array.from_param, 42) + self.assertRaises(TypeError, Structure.from_param, 42) + self.assertRaises(TypeError, Union.from_param, 42) + self.assertRaises(TypeError, _CFuncPtr.from_param, 42) + #self.assertRaises(TypeError, _Pointer.from_param, 42) + #self.assertRaises(TypeError, _SimpleCData.from_param, 42) ################################################################ diff --git a/Lib/ctypes/test/test_struct_fields.py b/Lib/ctypes/test/test_struct_fields.py index 22eb3b0cd7b8b9..8045cc82679cc1 100644 --- a/Lib/ctypes/test/test_struct_fields.py +++ b/Lib/ctypes/test/test_struct_fields.py @@ -46,5 +46,29 @@ class Y(X): Y._fields_ = [] self.assertRaises(AttributeError, setattr, X, "_fields_", []) + # __set__ and __get__ should raise a TypeError in case their self + # argument is not a ctype instance. + def test___set__(self): + class MyCStruct(Structure): + _fields_ = (("field", c_int),) + self.assertRaises(TypeError, + MyCStruct.field.__set__, 'wrong type self', 42) + + class MyCUnion(Union): + _fields_ = (("field", c_int),) + self.assertRaises(TypeError, + MyCUnion.field.__set__, 'wrong type self', 42) + + def test___get__(self): + class MyCStruct(Structure): + _fields_ = (("field", c_int),) + self.assertRaises(TypeError, + MyCStruct.field.__get__, 'wrong type self', 42) + + class MyCUnion(Union): + _fields_ = (("field", c_int),) + self.assertRaises(TypeError, + MyCUnion.field.__get__, 'wrong type self', 42) + if __name__ == "__main__": unittest.main() diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 833749a1c595cf..b51b1d2cf3a87c 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -924,6 +924,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (proto) { StgDictObject *itemdict = PyType_stgdict(proto); const char *current_format; + /* PyCPointerType_SetProto has verified proto has a stgdict. */ assert(itemdict); /* If itemdict->format is NULL, then this is a pointer to an incomplete type. We create a generic format string From 365a9c479d2076172b9411988267eb17ddcf9ba5 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Wed, 1 Mar 2017 01:29:25 +0200 Subject: [PATCH 2/5] added the rest of tests and patches. probably only a first draft. --- Lib/ctypes/test/test_funcptr.py | 5 +++++ Lib/ctypes/test/test_parameters.py | 4 ++-- Lib/ctypes/test/test_pointers.py | 5 +++++ Modules/_ctypes/_ctypes.c | 30 +++++++++++++++++++++++------- Modules/_ctypes/cfield.c | 12 ++++++++++-- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index f34734b164ebad..e0b9b54e97f673 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -123,5 +123,10 @@ def c_string(init): self.assertEqual(strtok(None, b"\n"), b"c") self.assertEqual(strtok(None, b"\n"), None) + def test_abstract(self): + from ctypes import _CFuncPtr + + self.assertRaises(TypeError, _CFuncPtr, 13, "name", 42, "iid") + if __name__ == '__main__': unittest.main() diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 48829cc3187ca5..4eaa15aff426de 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -177,8 +177,8 @@ def test_abstract(self): self.assertRaises(TypeError, Structure.from_param, 42) self.assertRaises(TypeError, Union.from_param, 42) self.assertRaises(TypeError, _CFuncPtr.from_param, 42) - #self.assertRaises(TypeError, _Pointer.from_param, 42) - #self.assertRaises(TypeError, _SimpleCData.from_param, 42) + self.assertRaises(TypeError, _Pointer.from_param, 42) + self.assertRaises(TypeError, _SimpleCData.from_param, 42) ################################################################ diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index 751f85fd11f59a..e97515879f1f0e 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -213,6 +213,11 @@ def test_pointer_type_str_name(self): from ctypes import _pointer_type_cache del _pointer_type_cache[id(P)] + def test_abstract(self): + from ctypes import _Pointer + + self.assertRaises(TypeError, _Pointer.set_type, 42) + if __name__ == '__main__': unittest.main() diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b51b1d2cf3a87c..ca606e75313779 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -971,7 +971,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) StgDictObject *dict; dict = PyType_stgdict((PyObject *)self); - assert(dict); + if (!dict) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } if (-1 == PyCPointerType_SetProto(dict, type)) return NULL; @@ -996,7 +1000,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value) } typedict = PyType_stgdict(type); - assert(typedict); /* Cannot be NULL for pointer types */ + if (!typedict) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } /* If we expect POINTER(), but receive a instance, accept it by calling byref(). @@ -2058,7 +2066,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) } dict = PyType_stgdict(type); - assert(dict); + if (!dict) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } /* I think we can rely on this being a one-character string */ fmt = PyUnicode_AsUTF8(dict->proto); @@ -3147,7 +3159,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) PyObject *argtypes; dict = PyType_stgdict((PyObject *)type); - assert(dict); /* Cannot be NULL. 'type' is a PyCFuncPtr type. */ + if (!dict) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return 0; + } argtypes = dict->argtypes; if (paramflags == NULL || dict->argtypes == NULL) @@ -4780,7 +4796,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) } stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL fr pointer instances */ + assert(stgdict); /* Cannot be NULL for pointer instances */ proto = stgdict->proto; assert(proto); @@ -4808,7 +4824,7 @@ Pointer_get_contents(CDataObject *self, void *closure) } stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL fr pointer instances */ + assert(stgdict); /* Cannot be NULL for pointer instances */ return PyCData_FromBaseObj(stgdict->proto, (PyObject *)self, 0, *(void **)self->b_ptr); @@ -4827,7 +4843,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) return -1; } stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL fr pointer instances */ + assert(stgdict); /* Cannot be NULL for pointer instances */ assert(stgdict->proto); if (!CDataObject_Check(value)) { int res = PyObject_IsInstance(value, stgdict->proto); diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index bb9b115bd2c48b..e2b9aa8ed153c5 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -205,7 +205,11 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) { CDataObject *dst; char *ptr; - assert(CDataObject_Check(inst)); + if (!CDataObject_Check(inst)) { + PyErr_SetString(PyExc_TypeError, + "not a ctype instance"); + return -1; + } dst = (CDataObject *)inst; ptr = dst->b_ptr + self->offset; if (value == NULL) { @@ -225,7 +229,11 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type) Py_INCREF(self); return (PyObject *)self; } - assert(CDataObject_Check(inst)); + if (!CDataObject_Check(inst)) { + PyErr_SetString(PyExc_TypeError, + "not a ctype instance"); + return NULL; + } src = (CDataObject *)inst; return PyCData_get(self->proto, self->getfunc, inst, self->index, self->size, src->b_ptr + self->offset); From c9e0ddf87d4a602fe4e1a57090fbd225714485d0 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Wed, 1 Mar 2017 09:48:31 +0200 Subject: [PATCH 3/5] removed trailing spaces --- Modules/_ctypes/_ctypes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ca606e75313779..9d42109b516ba9 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -975,7 +975,7 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) PyErr_SetString(PyExc_TypeError, "abstract class"); return NULL; - } + } if (-1 == PyCPointerType_SetProto(dict, type)) return NULL; From 6f0029c0b1f61c18c8cc45185cefaa60ae5bd1e7 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Thu, 2 Mar 2017 12:54:09 +0200 Subject: [PATCH 4/5] replace ctype with ctypes in error messages --- Modules/_ctypes/_ctypes.c | 2 +- Modules/_ctypes/cfield.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9d42109b516ba9..4a9ef2563dcc3c 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2915,7 +2915,7 @@ PyCData_set(PyObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, if (!CDataObject_Check(dst)) { PyErr_SetString(PyExc_TypeError, - "not a ctype instance"); + "not a ctypes instance"); return -1; } diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index e2b9aa8ed153c5..9f499537bbf076 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -207,7 +207,7 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) char *ptr; if (!CDataObject_Check(inst)) { PyErr_SetString(PyExc_TypeError, - "not a ctype instance"); + "not a ctypes instance"); return -1; } dst = (CDataObject *)inst; @@ -231,7 +231,7 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type) } if (!CDataObject_Check(inst)) { PyErr_SetString(PyExc_TypeError, - "not a ctype instance"); + "not a ctypes instance"); return NULL; } src = (CDataObject *)inst; From bb75d81897084d4597f96d570a5d8c38e7bb2b0c Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Thu, 2 Mar 2017 17:01:17 +0200 Subject: [PATCH 5/5] change back from ctypes instance to ctype instance --- Modules/_ctypes/_ctypes.c | 2 +- Modules/_ctypes/cfield.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 4a9ef2563dcc3c..9d42109b516ba9 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2915,7 +2915,7 @@ PyCData_set(PyObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, if (!CDataObject_Check(dst)) { PyErr_SetString(PyExc_TypeError, - "not a ctypes instance"); + "not a ctype instance"); return -1; } diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 9f499537bbf076..e2b9aa8ed153c5 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -207,7 +207,7 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) char *ptr; if (!CDataObject_Check(inst)) { PyErr_SetString(PyExc_TypeError, - "not a ctypes instance"); + "not a ctype instance"); return -1; } dst = (CDataObject *)inst; @@ -231,7 +231,7 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type) } if (!CDataObject_Check(inst)) { PyErr_SetString(PyExc_TypeError, - "not a ctypes instance"); + "not a ctype instance"); return NULL; } src = (CDataObject *)inst;