Skip to content

Commit 16757d3

Browse files
committed
finish sigsegv handling
1 parent 1751472 commit 16757d3

File tree

7 files changed

+57
-38
lines changed

7 files changed

+57
-38
lines changed

src/_pointers.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ def remove_ref(obj: Any) -> None: ...
77
def force_set_attr(typ: type[Any], key: str, value: Any) -> None: ...
88
def set_ref(obj: Any, count: int) -> None: ...
99
def handle(
10-
func: Callable[..., _T], args: tuple[Any, ...], kwargs: dict[str, Any]
10+
func: Callable[..., _T],
11+
args: tuple[Any, ...] | None = None,
12+
kwargs: dict[str, Any] | None = None,
1113
) -> _T: ...

src/mod.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#include <Python.h>
33
#include <signal.h>
44
#include <setjmp.h>
5+
#include <stdbool.h>
56
#define GETOBJ PyObject* obj; if (!PyArg_ParseTuple(args, "O", &obj)) return NULL
7+
#define CALL_ATTR(ob, attr) PyObject_Call(PyObject_GetAttrString(ob, attr), PyTuple_New(0), NULL)
68
static jmp_buf buf;
79

810
static PyObject* add_ref(PyObject* self, PyObject* args) {
@@ -44,32 +46,46 @@ void handler(int signum) {
4446

4547
static PyObject* handle(PyObject* self, PyObject* args) {
4648
PyObject* func;
47-
PyObject* params;
48-
PyObject* kwargs;
49+
PyObject* params = NULL;
50+
PyObject* kwargs = NULL;
51+
PyObject* faulthandler = PyImport_ImportModule("faulthandler");
4952

5053
if (!PyArg_ParseTuple(
5154
args,
52-
"O!O!O!",
53-
&PyFunction_Type,
55+
"O|O!O!",
5456
&func,
5557
&PyTuple_Type,
5658
&params,
5759
&PyDict_Type,
5860
&kwargs
5961
)
6062
) return NULL;
63+
64+
if (!params) params = PyTuple_New(0);
65+
if (!kwargs) kwargs = PyDict_New();
66+
6167
int val = setjmp(buf);
6268

69+
CALL_ATTR(faulthandler, "disable");
70+
// faulthandler needs to be shut off in case of a segfault or its message will still print
71+
6372
if (setjmp(buf)) {
73+
CALL_ATTR(faulthandler, "enable");
74+
PyCodeObject* code = PyFrame_GetCode(PyEval_GetFrame());
75+
6476
PyErr_Format(
6577
PyExc_RuntimeError,
6678
"segment violation occured during execution of %S",
67-
PyObject_GetAttrString(func, "__name__")
79+
code->co_name
6880
);
6981
return NULL;
7082
}
7183

7284
PyObject* result = PyObject_Call(func, params, kwargs);
85+
86+
if (!result) return NULL;
87+
88+
CALL_ATTR(faulthandler, "enable");
7389
return result;
7490
}
7591

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-a3"
30+
__version__ = "2.2.0-a4"

src/pointers/base_pointers.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
Any, Generic, Iterator, Optional, Tuple, Type, TypeVar, Union
1111
)
1212

13-
from _pointers import add_ref, remove_ref
13+
from typing_extensions import final
14+
15+
from _pointers import add_ref, handle, remove_ref
1416

1517
from ._utils import deref, force_set_attr, move_to_mem
1618
from .constants import NULL, Nullable
@@ -54,19 +56,22 @@ def __repr__(self) -> str:
5456
def __rich__(self):
5557
...
5658

59+
@final
5760
def __str__(self) -> str:
5861
return hex(self.address or 0)
5962

6063
@abstractmethod
6164
def _cleanup(self) -> None:
6265
...
6366

67+
@final
6468
def __eq__(self, data: object) -> bool:
6569
if not isinstance(data, BasePointer):
6670
return False
6771

6872
return data.address == self.address
6973

74+
@final
7075
def ensure(self) -> int:
7176
"""Ensure that the pointer is not null.
7277
@@ -91,20 +96,23 @@ def ensure(self) -> int:
9196

9297
class Movable(ABC, Generic[T, A]):
9398
@abstractmethod
94-
def move(
99+
def _move(
95100
self,
96101
data: Union[A, T],
97102
*,
98103
unsafe: bool = False,
99104
) -> None:
100-
"""Move data to the target address.
101-
102-
Args:
103-
data: Pointer or object to move into the current data.
104-
unsafe: Should buffer overflows be allowed.
105-
"""
106105
...
107106

107+
@final
108+
def move(
109+
self,
110+
data: Union[A, T],
111+
*,
112+
unsafe: bool = False,
113+
) -> None:
114+
handle(self._move, (data,), {"unsafe": unsafe})
115+
108116
def __ilshift__(self, data: Union[A, T]):
109117
self.move(data)
110118
return self
@@ -118,13 +126,18 @@ class Dereferencable(ABC, Generic[T]):
118126
"""Abstract class for an object that may be dereferenced."""
119127

120128
@abstractmethod
129+
def _dereference(self) -> T:
130+
...
131+
132+
@final
121133
def dereference(self) -> T:
122134
"""Dereference the pointer.
123135
124136
Returns:
125137
Value at the pointers address."""
126-
...
138+
return handle(self._dereference)
127139

140+
@final
128141
def __invert__(self) -> T:
129142
"""Dereference the pointer."""
130143
return self.dereference()
@@ -163,19 +176,10 @@ def __repr__(self) -> str:
163176
def __rich__(self):
164177
...
165178

166-
def __str__(self) -> str:
167-
return hex(self.address or 0)
168-
169179
@abstractmethod
170180
def _cleanup(self) -> None:
171181
...
172182

173-
def __eq__(self, data: object) -> bool:
174-
if not isinstance(data, BasePointer):
175-
return False
176-
177-
return data.address == self.address
178-
179183

180184
class Typed(ABC, Generic[T]):
181185
"""Base class for a pointer that has a type attribute."""
@@ -295,7 +299,7 @@ def assign(
295299
def address(self) -> Optional[int]:
296300
return self._address
297301

298-
def dereference(self) -> T:
302+
def _dereference(self) -> T:
299303
return deref(self.ensure())
300304

301305
def __irshift__(
@@ -362,7 +366,7 @@ def _make_stream_and_ptr(
362366
bytes_a = (ctypes.c_ubyte * size).from_address(address)
363367
return self.make_ct_pointer(), bytes(bytes_a)
364368

365-
def move(
369+
def _move(
366370
self,
367371
data: Union["BaseCPointer[T]", T],
368372
*,
@@ -432,7 +436,7 @@ def assigned(self) -> bool:
432436
def assigned(self, value: bool) -> None:
433437
self._assigned = value
434438

435-
def move(
439+
def _move(
436440
self,
437441
data: Union[BasePointer[T], T],
438442
unsafe: bool = False,
@@ -452,7 +456,7 @@ def move(
452456
self.assigned = True
453457
remove_ref(data)
454458

455-
def dereference(self) -> T:
459+
def _dereference(self) -> T:
456460
if self.freed:
457461
raise FreedMemoryError(
458462
"cannot dereference memory that has been freed",

src/pointers/c_pointer.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def size(self) -> int:
3535
def _as_parameter_(self) -> ctypes.c_void_p:
3636
return ctypes.c_void_p(self.address)
3737

38-
def dereference(self) -> Optional[int]:
38+
def _dereference(self) -> Optional[int]:
3939
"""Dereference the pointer."""
4040
deref = ctypes.c_void_p.from_address(self.ensure())
4141
return deref.value
@@ -131,7 +131,7 @@ def _as_parameter_(self):
131131

132132
return ctypes.pointer(deref)
133133

134-
def dereference(self) -> T:
134+
def _dereference(self) -> T:
135135
"""Dereference the pointer."""
136136
ctype = get_mapped(self.type)
137137

@@ -153,10 +153,6 @@ def __iter__(self) -> Iterator[T]:
153153
"""Dereference the pointer."""
154154
return iter({self.dereference()})
155155

156-
def __invert__(self) -> T:
157-
"""Dereference the pointer."""
158-
return self.dereference()
159-
160156

161157
class CArrayPointer(_CDeref[List[T]], Typed[Type[T]], BaseCPointer[List[T]]):
162158
"""Class representing a pointer to a C array."""
@@ -188,7 +184,7 @@ def _as_parameter_(self) -> "ctypes.Array[ctypes._CData]":
188184
deref = (ctype * self._length).from_address(self.ensure())
189185
return deref
190186

191-
def dereference(self) -> List[T]:
187+
def _dereference(self) -> List[T]:
192188
"""Dereference the pointer."""
193189
array = self._as_parameter_
194190
return [array[i] for i in range(self._length)] # type: ignore

src/pointers/object_pointer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def __repr__(self) -> str:
2323
def __rich__(self):
2424
return f"<pointer to [bold green]{self._get_tp_name()}[/] object at [cyan]{self}[/cyan]>" # noqa
2525

26-
def move(
26+
def _move(
2727
self,
2828
target: Union[T, "BasePointer[T]"],
2929
*,

tests/test_pointer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,5 @@ def _():
9696
def segfault():
9797
ctypes.string_at(0)
9898

99-
handle(segfault, (), {})
99+
with raises(RuntimeError):
100+
handle(segfault)

0 commit comments

Comments
 (0)