Skip to content

Commit 54968f1

Browse files
committed
abracadabra
1 parent 1b50b83 commit 54968f1

File tree

13 files changed

+186
-74
lines changed

13 files changed

+186
-74
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ Why would you ever need this
1010
- [Repository](https://github.com/ZeroIntensity/pointers.py)
1111
- [PyPI](https://pypi.org/project/pointers.py)
1212

13-
### Example
13+
### Examples
1414

1515
```py
16-
from pointers import to_ptr, Pointer, decay
16+
from pointers import Pointer, decay
1717

1818
a: str = '123'
1919
b: str = 'abc'
@@ -26,12 +26,17 @@ move(a, b)
2626
print(a, b) # abc abc
2727
```
2828

29-
#### Example with bindings
29+
```py
30+
from pointers import _
31+
32+
ptr = _&"hello world" # creates a new pointer object
33+
assert _*ptr == "hello world"
34+
```
3035

3136
```py
3237
from pointers import fopen, fprintf, fclose
3338

34-
file = fopen("/dev/null", "w")
39+
file = fopen("/dev/null", "w") # assigns file to the c FILE* type
3540
fprintf(file, "hello world")
3641
fclose(file)
3742
```

docs/getting_started.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ int main() {
5757
}
5858
```
5959

60+
## Using `_`
61+
62+
Instead of using `to_ptr`, you can use pointers.py's `_` object to replicate C/C++'s address-of operator:
63+
64+
```py
65+
from pointers import _
66+
67+
ptr = _&"test"
68+
```
69+
6070
## Decaying
6171

6272
While the pointer API is cool, having `to_ptr` everywhere isn't exactly great.

docs/index.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
- [Repository](https://github.com/ZeroIntensity/pointers.py)
66
- [PyPI](https://pypi.org/project/pointers.py)
77

8-
### Example
8+
### Examples
99

1010
```py
11-
from pointers import to_ptr, Pointer, decay
11+
from pointers import Pointer, decay
1212

1313
a: str = '123'
1414
b: str = 'abc'
@@ -21,6 +21,21 @@ move(a, b)
2121
print(a, b) # abc abc
2222
```
2323

24+
```py
25+
from pointers import _
26+
27+
ptr = _&"hello world" # creates a new pointer object
28+
assert _*ptr == "hello world"
29+
```
30+
31+
```py
32+
from pointers import fopen, fprintf, fclose
33+
34+
file = fopen("/dev/null", "w") # assigns file to the c FILE* type
35+
fprintf(file, "hello world")
36+
fclose(file)
37+
```
38+
2439
### Features
2540

2641
- Fully type safe

docs/pointers.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test(ptr: Pointer[str])
2828
print(~ptr) # much shorter and easier to type
2929
```
3030

31-
Finally, you can use the `*` operator, like in low level languages:
31+
You can also use the `*` operator, like in low level languages:
3232

3333
```py
3434
from pointers import Pointer, decay
@@ -70,6 +70,17 @@ def test(ptr: Pointer[list])
7070
test([1, 2, 3])
7171
```
7272

73+
## Using `_`
74+
75+
Like with the address-of operator, you can use `_` for dereferencing as well:
76+
77+
```py
78+
from pointers import _
79+
80+
ptr = _&"hello world"
81+
print(_*ptr)
82+
```
83+
7384
### Dereference Errors
7485

7586
In versions prior to 1.3.3, a `DereferenceError` could occur if the target object had been garbage collected.
@@ -159,7 +170,20 @@ int main() {
159170
}
160171
```
161172

173+
## Null Pointers
174+
175+
To create a null pointer, simply assign an existing pointer to `None`:
176+
177+
```py
178+
from pointers import to_ptr
179+
180+
a = to_ptr("a")
181+
a >>= None
182+
print(~a) # NullPointerError
183+
```
184+
162185
## Setting Attributes
186+
163187
If you would like to force setting an attribute on the targets type (or itself if you are pointing to a type), you can use `Pointer.set_attr`:
164188

165189
```py

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
if __name__ == "__main__":
77
setup(
88
name="pointers.py",
9-
version="1.3.9",
9+
version="1.4.0",
1010
author="ZeroIntensity",
1111
author_email="<zintensitydev@gmail.com>",
1212
description="Bringing the hell of pointers to Python.",

src/pointers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .pointer import Pointer, to_ptr, dereference_address, force_set_attr
1+
from .pointer import Pointer, to_ptr, dereference_address, force_set_attr, _
22
from .malloc import (
33
malloc,
44
IsMallocPointerError,

src/pointers/bindings.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,16 @@
3131
import ctypes
3232
from . import _cstd
3333
from .exceptions import InvalidBindingParameter
34+
from .pointer import Pointer
3435

3536
if TYPE_CHECKING:
3637
from .struct import Struct
3738

3839
T = TypeVar("T")
39-
PointerLike = Union[TypedCPointer[Any], VoidPointer]
40+
PointerLike = Union[TypedCPointer[Any], VoidPointer, None]
4041
StringLike = Union[str, bytes, VoidPointer, TypedCPointer[bytes]]
41-
Format = Union[str, bytes, PointerLike]
42+
Format = Union[StringLike, PointerLike]
43+
TypedPtr = Optional[TypedCPointer[T]]
4244

4345
__all__ = (
4446
"isalnum",
@@ -204,6 +206,19 @@ def _validate_args(
204206
if not isinstance(value, n_type):
205207
v_type = type(value)
206208

209+
if (n_type is Pointer) and (value is None):
210+
continue
211+
212+
if (
213+
typ
214+
in {
215+
ctypes.c_char_p,
216+
ctypes.c_void_p,
217+
ctypes.POINTER,
218+
}
219+
) and (value is None):
220+
continue
221+
207222
if ((v_type is ctypes.c_char_p) and (n_type is bytes)) or (
208223
issubclass(v_type, _BaseCPointer) and (typ is ctypes.c_void_p)
209224
):
@@ -317,15 +332,15 @@ def setlocale(category: int, locale: StringLike) -> str:
317332
return _base(dll.setlocale, category, _make_char_pointer(locale))
318333

319334

320-
def frexp(x: float, exponent: TypedCPointer[int]) -> int:
335+
def frexp(x: float, exponent: TypedPtr[int]) -> int:
321336
return _base(dll.frexp, x, exponent)
322337

323338

324339
def ldexp(x: float, exponent: int) -> int:
325340
return _base(dll.ldexp, x, exponent)
326341

327342

328-
def modf(x: float, integer: TypedCPointer[float]) -> int:
343+
def modf(x: float, integer: TypedPtr[float]) -> int:
329344
return _base(dll.modf, x, integer)
330345

331346

@@ -362,7 +377,7 @@ def fopen(filename: StringLike, mode: StringLike) -> VoidPointer:
362377

363378

364379
def fread(
365-
ptr: TypedCPointer[Any],
380+
ptr: PointerLike,
366381
size: int,
367382
nmemb: int,
368383
stream: PointerLike,
@@ -396,7 +411,7 @@ def ftell(stream: PointerLike) -> int:
396411

397412

398413
def fwrite(
399-
ptr: TypedCPointer[Any],
414+
ptr: PointerLike,
400415
size: int,
401416
nmemb: int,
402417
stream: PointerLike,
@@ -544,13 +559,13 @@ def perror(string: StringLike) -> None:
544559
return _base(dll.perror, _make_char_pointer(string))
545560

546561

547-
def strtod(string: StringLike, endptr: TypedCPointer[str]) -> int:
562+
def strtod(string: StringLike, endptr: PointerLike) -> int:
548563
return _base(dll.strtod, _make_char_pointer(string), endptr)
549564

550565

551566
def strtol(
552567
string: StringLike,
553-
endptr: TypedCPointer[str],
568+
endptr: PointerLike,
554569
base: int,
555570
) -> int:
556571
return _base(
@@ -563,7 +578,7 @@ def strtol(
563578

564579
def strtoul(
565580
string: StringLike,
566-
endptr: TypedCPointer[str],
581+
endptr: PointerLike,
567582
base: int,
568583
) -> int:
569584
return _base(
@@ -640,35 +655,35 @@ def wctomb(string: StringLike, wchar: str) -> int:
640655
return _base(dll.wctomb, _make_char_pointer(string), wchar)
641656

642657

643-
def memchr(string: TypedCPointer[Any], c: int, n: int) -> VoidPointer:
658+
def memchr(string: PointerLike, c: int, n: int) -> VoidPointer:
644659
return _base(dll.memchr, string, c, n)
645660

646661

647662
def memcmp(
648-
str1: TypedCPointer[Any],
649-
str2: TypedCPointer[Any],
663+
str1: PointerLike,
664+
str2: PointerLike,
650665
n: int,
651666
) -> int:
652667
return _base(dll.memcmp, str1, str2, n)
653668

654669

655670
def memcpy(
656-
dest: TypedCPointer[Any],
657-
src: TypedCPointer[Any],
671+
dest: PointerLike,
672+
src: PointerLike,
658673
n: int,
659674
) -> VoidPointer:
660675
return _base(dll.memcpy, dest, src, n)
661676

662677

663678
def memmove(
664-
dest: TypedCPointer[Any],
665-
src: TypedCPointer[Any],
679+
dest: PointerLike,
680+
src: PointerLike,
666681
n: int,
667682
) -> VoidPointer:
668683
return _base(dll.memmove, dest, src, n)
669684

670685

671-
def memset(string: TypedCPointer[Any], c: int, n: int) -> VoidPointer:
686+
def memset(string: PointerLike, c: int, n: int) -> VoidPointer:
672687
return _base(dll.memset, string, c, n)
673688

674689

@@ -804,7 +819,7 @@ def clock() -> int:
804819
return _base(dll.clock)
805820

806821

807-
def ctime(timer: TypedCPointer[int]) -> str:
822+
def ctime(timer: TypedPtr[int]) -> str:
808823
return _base(dll.ctime, timer)
809824

810825

@@ -831,7 +846,7 @@ def strftime(
831846
)
832847

833848

834-
def time(timer: TypedCPointer[int]) -> int:
849+
def time(timer: TypedPtr[int]) -> int:
835850
return _base(dll.time, timer)
836851

837852

src/pointers/c_pointer.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ def _as_parameter_(self) -> Union[int, ctypes.pointer]:
7878
if existing:
7979
return ctypes.pointer(existing.struct)
8080

81-
return self._address
81+
return self.ensure()
8282

8383
def __repr__(self) -> str:
84-
return f"<pointer to struct at {hex(self.address)}>"
84+
return f"<pointer to struct at {str(self)}>"
8585

8686

8787
class _BaseCPointer(Pointer[Any], Generic[T]):
@@ -112,7 +112,7 @@ def _make_stream_and_ptr(
112112
self,
113113
data: "_BaseCPointer",
114114
) -> Tuple[ctypes.pointer, bytes]:
115-
bytes_a = (ctypes.c_ubyte * data.size).from_address(data.address)
115+
bytes_a = (ctypes.c_ubyte * data.size).from_address(data.ensure())
116116

117117
return self.make_ct_pointer(), bytes(bytes_a)
118118

@@ -128,7 +128,7 @@ def move(self, data: Pointer[T], unsafe: bool = False) -> None:
128128

129129
def make_ct_pointer(self):
130130
return ctypes.cast(
131-
self.address,
131+
self.ensure(),
132132
ctypes.POINTER(ctypes.c_char * self.size),
133133
)
134134

@@ -223,16 +223,14 @@ def _as_parameter_(self) -> ctypes.c_void_p:
223223

224224
def dereference(self) -> Optional[int]:
225225
"""Dereference the pointer."""
226-
deref = ctypes.c_void_p.from_address(self.address)
226+
deref = ctypes.c_void_p.from_address(self.ensure())
227227
return deref.value
228228

229229
def __repr__(self) -> str:
230-
return f"<void pointer to {hex(self.address)}>" # noqa
230+
return f"<void pointer to {str(self)}>" # noqa
231231

232232
def __rich__(self):
233-
return (
234-
f"<[green]void[/green] pointer to [cyan]{hex(self.address)}[/cyan]>" # noqa
235-
)
233+
return f"<[green]void[/green] pointer to [cyan]{str(self)}[/cyan]>" # noqa
236234

237235
def __del__(self):
238236
pass
@@ -257,14 +255,14 @@ def __init__(
257255
@property
258256
def _as_parameter_(self) -> "ctypes.pointer[ctypes._CData]":
259257
ctype = self.get_mapped(self.type)
260-
deref = ctype.from_address(self.address)
258+
deref = ctype.from_address(self.ensure())
261259
return ctypes.pointer(deref)
262260

263261
def dereference(self) -> T:
264262
"""Dereference the pointer."""
265263
ctype = self.get_mapped(self.type)
266264
ptr = (
267-
ctype.from_address(self.address)
265+
ctype.from_address(self.ensure())
268266
if not self._alt
269267
else ctype(self.address) # fmt: off
270268
)
@@ -278,10 +276,10 @@ def move(self, data: Pointer, unsafe: bool = False) -> None:
278276
super().move(data, unsafe)
279277

280278
def __repr__(self) -> str:
281-
return f"<typed c pointer to {hex(self.address)}>" # noqa
279+
return f"<typed c pointer to {str(self)}>" # noqa
282280

283281
def __rich__(self):
284-
return f"<[green]typed c[/green] pointer to [cyan]{hex(self.address)}[/cyan]>" # noqa
282+
return f"<[green]typed c[/green] pointer to [cyan]{str(self)}[/cyan]>" # noqa
285283

286284
def __del__(self):
287285
if (self.type is not str) and (self._decref):
@@ -291,7 +289,7 @@ def __del__(self):
291289

292290
def cast(ptr: VoidPointer, data_type: Type[T]) -> TypedCPointer[T]:
293291
"""Cast a void pointer to a typed pointer."""
294-
return TypedCPointer(ptr.address, data_type, ptr.size, decref=False)
292+
return TypedCPointer(ptr.ensure(), data_type, ptr.size, decref=False)
295293

296294

297295
def to_c_ptr(data: T) -> TypedCPointer[T]:

0 commit comments

Comments
 (0)