Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit faecc38

Browse files
committed
Issue #11241: subclasses of ctypes.Array can now be subclassed.
1 parent 326e189 commit faecc38

3 files changed

Lines changed: 93 additions & 40 deletions

File tree

Lib/ctypes/test/test_arrays.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,57 @@ class my_int(c_int):
127127
t2 = my_int * 1
128128
self.assertTrue(t1 is t2)
129129

130+
def test_subclass(self):
131+
class T(Array):
132+
_type_ = c_int
133+
_length_ = 13
134+
class U(T):
135+
pass
136+
class V(U):
137+
pass
138+
class W(V):
139+
pass
140+
class X(T):
141+
_type_ = c_short
142+
class Y(T):
143+
_length_ = 187
144+
145+
for c in [T, U, V, W]:
146+
self.assertEqual(c._type_, c_int)
147+
self.assertEqual(c._length_, 13)
148+
self.assertEqual(c()._type_, c_int)
149+
self.assertEqual(c()._length_, 13)
150+
151+
self.assertEqual(X._type_, c_short)
152+
self.assertEqual(X._length_, 13)
153+
self.assertEqual(X()._type_, c_short)
154+
self.assertEqual(X()._length_, 13)
155+
156+
self.assertEqual(Y._type_, c_int)
157+
self.assertEqual(Y._length_, 187)
158+
self.assertEqual(Y()._type_, c_int)
159+
self.assertEqual(Y()._length_, 187)
160+
161+
def test_bad_subclass(self):
162+
import sys
163+
164+
with self.assertRaises(AttributeError):
165+
class T(Array):
166+
pass
167+
with self.assertRaises(AttributeError):
168+
class T(Array):
169+
_type_ = c_int
170+
with self.assertRaises(AttributeError):
171+
class T(Array):
172+
_length_ = 13
173+
with self.assertRaises(OverflowError):
174+
class T(Array):
175+
_type_ = c_int
176+
_length_ = sys.maxsize * 2
177+
with self.assertRaises(AttributeError):
178+
class T(Array):
179+
_type_ = c_int
180+
_length_ = 1.87
181+
130182
if __name__ == '__main__':
131183
unittest.main()

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ Library
185185
Extension Modules
186186
-----------------
187187

188+
- Issue #11241: subclasses of ctypes.Array can now be subclassed.
189+
188190
- Issue #9651: Fix a crash when ctypes.create_string_buffer(0) was passed to
189191
some functions like file.write().
190192

Modules/_ctypes/_ctypes.c

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,49 +1256,57 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
12561256
PyTypeObject *result;
12571257
StgDictObject *stgdict;
12581258
StgDictObject *itemdict;
1259-
PyObject *proto;
1260-
PyObject *typedict;
1259+
PyObject *length_attr, *type_attr;
12611260
long length;
12621261
int overflow;
12631262
Py_ssize_t itemsize, itemalign;
12641263
char buf[32];
12651264

1266-
typedict = PyTuple_GetItem(args, 2);
1267-
if (!typedict)
1265+
/* create the new instance (which is a class,
1266+
since we are a metatype!) */
1267+
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
1268+
if (result == NULL)
12681269
return NULL;
12691270

1270-
proto = PyDict_GetItemString(typedict, "_length_"); /* Borrowed ref */
1271-
if (!proto || !PyLong_Check(proto)) {
1271+
/* Initialize these variables to NULL so that we can simplify error
1272+
handling by using Py_XDECREF. */
1273+
stgdict = NULL;
1274+
type_attr = NULL;
1275+
1276+
length_attr = PyObject_GetAttrString((PyObject *)result, "_length_");
1277+
if (!length_attr || !PyLong_Check(length_attr)) {
12721278
PyErr_SetString(PyExc_AttributeError,
12731279
"class must define a '_length_' attribute, "
12741280
"which must be a positive integer");
1275-
return NULL;
1281+
Py_XDECREF(length_attr);
1282+
goto error;
12761283
}
1277-
length = PyLong_AsLongAndOverflow(proto, &overflow);
1284+
length = PyLong_AsLongAndOverflow(length_attr, &overflow);
12781285
if (overflow) {
12791286
PyErr_SetString(PyExc_OverflowError,
12801287
"The '_length_' attribute is too large");
1281-
return NULL;
1288+
Py_DECREF(length_attr);
1289+
goto error;
12821290
}
1291+
Py_DECREF(length_attr);
12831292

1284-
proto = PyDict_GetItemString(typedict, "_type_"); /* Borrowed ref */
1285-
if (!proto) {
1293+
type_attr = PyObject_GetAttrString((PyObject *)result, "_type_");
1294+
if (!type_attr) {
12861295
PyErr_SetString(PyExc_AttributeError,
12871296
"class must define a '_type_' attribute");
1288-
return NULL;
1297+
goto error;
12891298
}
12901299

12911300
stgdict = (StgDictObject *)PyObject_CallObject(
12921301
(PyObject *)&PyCStgDict_Type, NULL);
12931302
if (!stgdict)
1294-
return NULL;
1303+
goto error;
12951304

1296-
itemdict = PyType_stgdict(proto);
1305+
itemdict = PyType_stgdict(type_attr);
12971306
if (!itemdict) {
12981307
PyErr_SetString(PyExc_TypeError,
12991308
"_type_ must have storage info");
1300-
Py_DECREF((PyObject *)stgdict);
1301-
return NULL;
1309+
goto error;
13021310
}
13031311

13041312
assert(itemdict->format);
@@ -1309,16 +1317,12 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13091317
sprintf(buf, "(%ld)", length);
13101318
stgdict->format = _ctypes_alloc_format_string(buf, itemdict->format);
13111319
}
1312-
if (stgdict->format == NULL) {
1313-
Py_DECREF((PyObject *)stgdict);
1314-
return NULL;
1315-
}
1320+
if (stgdict->format == NULL)
1321+
goto error;
13161322
stgdict->ndim = itemdict->ndim + 1;
13171323
stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t *) * stgdict->ndim);
1318-
if (stgdict->shape == NULL) {
1319-
Py_DECREF((PyObject *)stgdict);
1320-
return NULL;
1321-
}
1324+
if (stgdict->shape == NULL)
1325+
goto error;
13221326
stgdict->shape[0] = length;
13231327
memmove(&stgdict->shape[1], itemdict->shape,
13241328
sizeof(Py_ssize_t) * (stgdict->ndim - 1));
@@ -1327,7 +1331,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13271331
if (length * itemsize < 0) {
13281332
PyErr_SetString(PyExc_OverflowError,
13291333
"array too large");
1330-
return NULL;
1334+
goto error;
13311335
}
13321336

13331337
itemalign = itemdict->align;
@@ -1338,26 +1342,16 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13381342
stgdict->size = itemsize * length;
13391343
stgdict->align = itemalign;
13401344
stgdict->length = length;
1341-
Py_INCREF(proto);
1342-
stgdict->proto = proto;
1345+
stgdict->proto = type_attr;
13431346

13441347
stgdict->paramfunc = &PyCArrayType_paramfunc;
13451348

13461349
/* Arrays are passed as pointers to function calls. */
13471350
stgdict->ffi_type_pointer = ffi_type_pointer;
13481351

1349-
/* create the new instance (which is a class,
1350-
since we are a metatype!) */
1351-
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
1352-
if (result == NULL)
1353-
return NULL;
1354-
13551352
/* replace the class dict by our updated spam dict */
1356-
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
1357-
Py_DECREF(result);
1358-
Py_DECREF((PyObject *)stgdict);
1359-
return NULL;
1360-
}
1353+
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict))
1354+
goto error;
13611355
Py_DECREF(result->tp_dict);
13621356
result->tp_dict = (PyObject *)stgdict;
13631357

@@ -1366,15 +1360,20 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13661360
*/
13671361
if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
13681362
if (-1 == add_getset(result, CharArray_getsets))
1369-
return NULL;
1363+
goto error;
13701364
#ifdef CTYPES_UNICODE
13711365
} else if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
13721366
if (-1 == add_getset(result, WCharArray_getsets))
1373-
return NULL;
1367+
goto error;
13741368
#endif
13751369
}
13761370

13771371
return (PyObject *)result;
1372+
error:
1373+
Py_XDECREF((PyObject*)stgdict);
1374+
Py_XDECREF(type_attr);
1375+
Py_DECREF(result);
1376+
return NULL;
13781377
}
13791378

13801379
PyTypeObject PyCArrayType_Type = {

0 commit comments

Comments
 (0)