Skip to content

Commit 0b519c9

Browse files
committed
updates/patches with c pointers
1 parent 0296f11 commit 0b519c9

File tree

7 files changed

+150
-33
lines changed

7 files changed

+150
-33
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ classifiers = [
2323
dependencies = [
2424
"typing_extensions",
2525
]
26-
version = "2.2.0-a"
26+
version = "2.2.0-a2"

src/pointers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@
2727
from .std_structs import DivT, Lconv, LDivT, Tm
2828
from .structure import Struct, StructPointer
2929

30-
__version__ = "2.2.0-a"
30+
__version__ = "2.2.0-a2"

src/pointers/_utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
int: ctypes.c_int,
2222
float: ctypes.c_float,
2323
bool: ctypes.c_bool,
24-
Any: ctypes.c_void_p, # type: ignore
2524
}
2625

2726
_PY_TYPES: Dict[Type["ctypes._CData"], type] = {
@@ -84,7 +83,11 @@ def map_type(data: Any) -> "ctypes._CData":
8483

8584
def get_mapped(typ: Any) -> "Type[ctypes._CData]":
8685
"""Get the C mapped value of the given type."""
87-
return _C_TYPES.get(typ) or ctypes.py_object
86+
from .c_pointer import VoidPointer
87+
88+
return {**_C_TYPES, VoidPointer: ctypes.c_void_p}.get( # type: ignore
89+
typ,
90+
) or ctypes.py_object
8891

8992

9093
def is_mappable(typ: Any) -> bool:

src/pointers/c_pointer.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ def address(self) -> Optional[int]:
142142
def _as_parameter_(self):
143143
ctype = get_mapped(self.type)
144144
deref = ctype.from_address(self.ensure())
145+
value = deref.value # type: ignore
146+
147+
if isinstance(value, (TypedCPointer, VoidPointer)):
148+
return ctypes.pointer(value._as_parameter_)
149+
145150
return ctypes.pointer(deref)
146151

147152
def dereference(self) -> T:

src/pointers/std_structs.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import ctypes
2-
from typing import Any, Dict, Type
2+
from typing import TYPE_CHECKING, Dict, Type
33

44
from ._cstd import div_t, lconv, ldiv_t, tm
5-
from ._pyapi import PyType_Slot
6-
from .structure import Struct
5+
from ._pyapi import PyType_Slot, PyType_Spec
6+
from .structure import Struct, StructPointer
7+
8+
if TYPE_CHECKING:
9+
from .c_pointer import VoidPointer
710

811
__all__ = (
912
"Tm",
@@ -56,7 +59,15 @@ class Lconv(Struct):
5659

5760
class PyTypeSlot(Struct):
5861
slot: int
59-
pfunc: Any
62+
pfunc: "VoidPointer"
63+
64+
65+
class PyTypeSpec(Struct):
66+
name: bytes
67+
basic_size: int
68+
itemsize: int
69+
flags: int
70+
slots: StructPointer[PyTypeSlot]
6071

6172

6273
STRUCT_MAP: Dict[Type[ctypes.Structure], Type[Struct]] = {
@@ -65,4 +76,5 @@ class PyTypeSlot(Struct):
6576
ldiv_t: LDivT,
6677
lconv: Lconv,
6778
PyType_Slot: PyTypeSlot,
79+
PyType_Spec: PyTypeSpec,
6880
}

src/pointers/structure.py

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import ctypes
22
from contextlib import suppress
3-
from typing import Any, Optional, Type, TypeVar, Union, get_type_hints
3+
from typing import Any, List, Optional, Type, TypeVar, Union, get_type_hints
44

5-
from ._utils import attempt_decode, get_mapped
6-
from .c_pointer import BaseCPointer
5+
from _pointers import add_ref
6+
7+
from ._utils import attempt_decode, get_mapped, get_py
8+
from .base_pointers import BaseCPointer
9+
from .c_pointer import TypedCPointer, VoidPointer
710
from .constants import RawType
811
from .object_pointer import Pointer
912

@@ -15,21 +18,40 @@
1518
)
1619

1720

18-
def _get_type(
19-
class_: Type["Struct"],
20-
ct: Type["ctypes._CData"],
21-
name: str,
22-
) -> Type["ctypes._CData"]:
23-
attr = getattr(class_, name, None)
21+
class Struct:
22+
"""Abstract class representing a struct."""
2423

25-
if isinstance(attr, RawType):
26-
return attr.tp
24+
def _convert_tc_ptr(self, typ: Any, name: str):
25+
if typ is TypedCPointer:
26+
raise TypeError(
27+
"cannot instantiate: TypedCPointer has no type argument",
28+
)
2729

28-
return ct
30+
if getattr(typ, "__origin__", None) is TypedCPointer:
31+
setattr(
32+
type(self),
33+
name,
34+
RawType(
35+
ctypes.POINTER(get_mapped(typ.__args__[0])),
36+
),
37+
)
2938

39+
return typ
3040

31-
class Struct:
32-
"""Abstract class representing a struct."""
41+
def _get_type(
42+
self,
43+
ct: Type["ctypes._CData"],
44+
name: str,
45+
) -> Type["ctypes._CData"]:
46+
attr = getattr(type(self), name, None)
47+
48+
if isinstance(attr, RawType):
49+
return attr.tp
50+
51+
if ct is ctypes.c_void_p:
52+
self._void_p.append(name)
53+
54+
return ct
3355

3456
def __init__(self, *args: Any, do_sync: bool = True):
3557
class_typ: Type[Struct] = type(self)
@@ -40,15 +62,16 @@ def __init__(self, *args: Any, do_sync: bool = True):
4062
)
4163

4264
hints = get_type_hints(class_typ)
43-
self._hints = hints
65+
self._void_p: List[str] = []
66+
self._hints = {k: self._convert_tc_ptr(v, k) for k, v in hints.items()}
4467

4568
class _InternalStruct(ctypes.Structure):
4669
_fields_ = [
4770
(
4871
name,
49-
_get_type(class_typ, get_mapped(typ), name),
72+
self._get_type(get_mapped(typ), name),
5073
)
51-
for name, typ in hints.items() # fmt: off
74+
for name, typ in self._hints.items() # fmt: off
5275
]
5376

5477
self._struct = _InternalStruct(
@@ -91,6 +114,29 @@ def __getattribute__(self, name: str):
91114
if (name in hints) and (type(attr)) is bytes:
92115
attr = attempt_decode(attr)
93116

117+
if isinstance(attr, ctypes._Pointer): # type: ignore
118+
value = attr.contents
119+
ct = type(value)
120+
121+
if ct is ctypes.c_void_p:
122+
add_ref(ct)
123+
return VoidPointer(
124+
ctypes.addressof(value),
125+
ctypes.sizeof(value),
126+
)
127+
128+
py_type = get_py(ct)
129+
return TypedCPointer(
130+
ctypes.addressof(value),
131+
py_type,
132+
ctypes.sizeof(value),
133+
False,
134+
)
135+
136+
if name in super().__getattribute__("_void_p"):
137+
ct = ctypes.c_void_p(attr) # type: ignore
138+
return VoidPointer(attr, ctypes.sizeof(ct)) # type: ignore
139+
94140
return attr
95141

96142
def __setattr__(self, name: str, value: Any):

tests/test_bindings.py

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from ward import raises, test
22

3-
from pointers import InvalidBindingParameter
3+
from pointers import (
4+
InvalidBindingParameter, Struct, StructPointer, TypedCPointer, VoidPointer
5+
)
46
from pointers import _cstd as std
57
from pointers import (
68
binds, c_free, c_malloc, cast, div, isspace, signal, sprintf, strcpy,
7-
strlen, toupper
9+
strlen, to_c_ptr, to_struct_ptr, to_voidp, toupper
810
)
9-
from pointers._cstd import DivT
11+
from pointers.std_structs import DivT
1012

1113

1214
@test("c strings")
@@ -55,12 +57,44 @@ def _():
5557
assert type(res) is DivT
5658
assert res.quot == 10
5759

60+
class A(Struct):
61+
one: int
62+
two: int
5863

59-
@test("struct pointers")
60-
def _():
61-
res = div(10, 1)
62-
assert type(res) is DivT
63-
assert res.quot == 10
64+
a = A(1, 2)
65+
66+
class MyStruct(Struct):
67+
a: str
68+
b: str
69+
c: StructPointer[A]
70+
d: TypedCPointer[int]
71+
e: VoidPointer
72+
73+
s = MyStruct(
74+
"a",
75+
"b",
76+
to_struct_ptr(a),
77+
to_c_ptr(1),
78+
to_voidp(
79+
to_c_ptr("hello"),
80+
),
81+
)
82+
83+
assert s.a == "a"
84+
assert type(s.c) is StructPointer
85+
assert type(s.d) is TypedCPointer
86+
assert type(s.e) is VoidPointer
87+
88+
assert (~s.c) is a
89+
assert (~s.c).one == a.one
90+
assert ~s.d == 1
91+
assert ~cast(s.e, str) == "hello"
92+
93+
class Foo(Struct):
94+
bar: TypedCPointer
95+
96+
with raises(TypeError):
97+
Foo()
6498

6599

66100
@test("custom bindings")
@@ -87,3 +121,20 @@ def _():
87121
isspace("")
88122

89123
assert isspace(" ") != 0
124+
125+
126+
@test("c pointers")
127+
def _():
128+
ptr = to_c_ptr(1)
129+
ptr2 = to_c_ptr("hi")
130+
131+
assert ~ptr == 1
132+
assert ~ptr2 == "hi"
133+
134+
double_ptr = to_c_ptr(to_c_ptr(1))
135+
assert type(~double_ptr) is TypedCPointer
136+
assert ~(~double_ptr) == 1
137+
voidp = to_voidp(to_c_ptr(1))
138+
assert type(voidp) is VoidPointer
139+
140+
assert ~cast(voidp, int) == 1

0 commit comments

Comments
 (0)