Skip to content

Commit 3754d3d

Browse files
Implement pointer-type creation via __pointer_type__
1 parent 6ea410d commit 3754d3d

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

Lib/ctypes/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,28 @@ class c_bool(_SimpleCData):
266266

267267
from _ctypes import POINTER, pointer, _pointer_type_cache
268268

269+
CType_Type = _Pointer.__base__
270+
271+
def POINTER(cls):
272+
if cls is None:
273+
return c_void_p
274+
try:
275+
pt = cls.__pointer_type__
276+
if pt is not None:
277+
return pt
278+
except AttributeError:
279+
pass
280+
if isinstance(cls, str):
281+
return type(f'LP_{cls}', (_Pointer,), {})
282+
if issubclass(cls, CType_Type):
283+
return type(f'LP_{cls.__name__}', (_Pointer,), {'_type_': cls})
284+
285+
raise TypeError(f'must be a ctypes-like type: {cls}')
286+
287+
def pointer(arg):
288+
typ = POINTER(type(arg))
289+
return typ(arg)
290+
269291
class c_wchar_p(_SimpleCData):
270292
_type_ = "Z"
271293
def __repr__(self):

Lib/test/test_ctypes/test_c_simple_type_meta.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __new__(cls, name, bases, namespace):
3636
else:
3737
ptr_bases = (self, POINTER(bases[0]))
3838
p = p_meta(f"POINTER({self.__name__})", ptr_bases, {})
39-
ctypes._pointer_type_cache[self] = p
39+
cls.__pointer_type__ = p
4040
return self
4141

4242
class p_meta(PyCSimpleType, ct_meta):
@@ -69,7 +69,7 @@ def __new__(cls, name, bases, namespace):
6969
if isinstance(self, p_meta):
7070
return self
7171
p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {})
72-
ctypes._pointer_type_cache[self] = p
72+
cls.__pointer_type__ = p
7373
return self
7474

7575
class p_meta(PyCSimpleType, ct_meta):
@@ -103,7 +103,7 @@ def __init__(self, name, bases, namespace):
103103
else:
104104
ptr_bases = (self, POINTER(bases[0]))
105105
p = p_meta(f"POINTER({self.__name__})", ptr_bases, {})
106-
ctypes._pointer_type_cache[self] = p
106+
type(self).__pointer_type__ = p
107107

108108
class p_meta(PyCSimpleType, ct_meta):
109109
pass
@@ -135,7 +135,7 @@ def __init__(self, name, bases, namespace):
135135
if isinstance(self, p_meta):
136136
return
137137
p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {})
138-
ctypes._pointer_type_cache[self] = p
138+
type(self).__pointer_type__ = p
139139

140140
class p_meta(PyCSimpleType, ct_meta):
141141
pass

Modules/_ctypes/_ctypes.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,21 @@ _ctypes_CType_Type___sizeof___impl(PyObject *self, PyTypeObject *cls)
564564
return PyLong_FromSsize_t(size);
565565
}
566566

567+
static PyObject *
568+
ctype_get_pointer_type(PyObject *self, void *Py_UNUSED(ignored))
569+
{
570+
ctypes_state *st = get_module_state_by_def(Py_TYPE(self));
571+
StgInfo *info;
572+
if (PyStgInfo_FromType(st, self, &info) < 0) {
573+
return NULL;
574+
}
575+
assert(info); /* Cannot be NULL */
576+
if (info->pointer_type) {
577+
return Py_NewRef(info->pointer_type);
578+
}
579+
Py_RETURN_NONE;
580+
}
581+
567582
static PyObject *
568583
CType_Type_repeat(PyObject *self, Py_ssize_t length);
569584

@@ -573,12 +588,18 @@ static PyMethodDef ctype_methods[] = {
573588
{0},
574589
};
575590

591+
static PyGetSetDef ctype_getsets[] = {
592+
{ "__pointer_type__", ctype_get_pointer_type, NULL, "pointer type", NULL },
593+
{ NULL, NULL }
594+
};
595+
576596
static PyType_Slot ctype_type_slots[] = {
577597
{Py_tp_token, Py_TP_USE_SPEC},
578598
{Py_tp_traverse, CType_Type_traverse},
579599
{Py_tp_clear, CType_Type_clear},
580600
{Py_tp_dealloc, CType_Type_dealloc},
581601
{Py_tp_methods, ctype_methods},
602+
{Py_tp_getset, ctype_getsets},
582603
// Sequence protocol.
583604
{Py_sq_repeat, CType_Type_repeat},
584605
{0, NULL},
@@ -1174,7 +1195,7 @@ class _ctypes.PyCPointerType "PyObject *" "clinic_state()->PyCPointerType_Type"
11741195

11751196

11761197
static int
1177-
PyCPointerType_SetProto(ctypes_state *st, StgInfo *stginfo, PyObject *proto)
1198+
PyCPointerType_SetProto(ctypes_state *st, PyObject *self, StgInfo *stginfo, PyObject *proto)
11781199
{
11791200
if (!proto || !PyType_Check(proto)) {
11801201
PyErr_SetString(PyExc_TypeError,
@@ -1190,8 +1211,17 @@ PyCPointerType_SetProto(ctypes_state *st, StgInfo *stginfo, PyObject *proto)
11901211
"_type_ must have storage info");
11911212
return -1;
11921213
}
1214+
if (info->pointer_type) {
1215+
PyErr_Format(PyExc_TypeError,
1216+
"pointer type already set: old=%R, new=%R",
1217+
info->pointer_type, self);
1218+
return -1;
1219+
}
1220+
11931221
Py_INCREF(proto);
11941222
Py_XSETREF(stginfo->proto, proto);
1223+
1224+
Py_XSETREF(info->pointer_type, Py_NewRef(self));
11951225
return 0;
11961226
}
11971227

@@ -1243,7 +1273,7 @@ PyCPointerType_init(PyObject *self, PyObject *args, PyObject *kwds)
12431273
}
12441274
if (proto) {
12451275
const char *current_format;
1246-
if (PyCPointerType_SetProto(st, stginfo, proto) < 0) {
1276+
if (PyCPointerType_SetProto(st, self, stginfo, proto) < 0) {
12471277
Py_DECREF(proto);
12481278
return -1;
12491279
}
@@ -1307,7 +1337,7 @@ PyCPointerType_set_type_impl(PyTypeObject *self, PyTypeObject *cls,
13071337
return NULL;
13081338
}
13091339

1310-
if (PyCPointerType_SetProto(st, info, type) < 0) {
1340+
if (PyCPointerType_SetProto(st, (PyObject *)self, info, type) < 0) {
13111341
Py_DECREF(attrdict);
13121342
return NULL;
13131343
}

Modules/_ctypes/callproc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2033,7 +2033,7 @@ create_pointer_type(PyObject *module, PyObject *cls)
20332033
}
20342034

20352035
if (info) {
2036-
info->pointer_type = Py_NewRef(result);
2036+
// info->pointer_type = Py_NewRef(result);
20372037
}
20382038

20392039
return result;

0 commit comments

Comments
 (0)