Skip to content

Commit 1751472

Browse files
committed
i dont have a good way to test features on other os's
1 parent 0681323 commit 1751472

File tree

4 files changed

+67
-8
lines changed

4 files changed

+67
-8
lines changed

src/_pointers.pyi

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
from typing import Any, Type
1+
from typing import Any, Callable, TypeVar
2+
3+
_T = TypeVar("_T")
24

35
def add_ref(obj: Any) -> None: ...
46
def remove_ref(obj: Any) -> None: ...
5-
def force_set_attr(typ: Type[Any], key: str, value: Any) -> None: ...
7+
def force_set_attr(typ: type[Any], key: str, value: Any) -> None: ...
68
def set_ref(obj: Any, count: int) -> None: ...
9+
def handle(
10+
func: Callable[..., _T], args: tuple[Any, ...], kwargs: dict[str, Any]
11+
) -> _T: ...

src/mod.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#define PY_SSIZE_T_CLEAN
22
#include <Python.h>
3+
#include <signal.h>
4+
#include <setjmp.h>
35
#define GETOBJ PyObject* obj; if (!PyArg_ParseTuple(args, "O", &obj)) return NULL
6+
static jmp_buf buf;
47

58
static PyObject* add_ref(PyObject* self, PyObject* args) {
69
GETOBJ;
@@ -35,13 +38,47 @@ static PyObject* force_set_attr(PyObject* self, PyObject* args) {
3538
Py_RETURN_NONE;
3639
}
3740

41+
void handler(int signum) {
42+
longjmp(buf, 1);
43+
}
44+
45+
static PyObject* handle(PyObject* self, PyObject* args) {
46+
PyObject* func;
47+
PyObject* params;
48+
PyObject* kwargs;
49+
50+
if (!PyArg_ParseTuple(
51+
args,
52+
"O!O!O!",
53+
&PyFunction_Type,
54+
&func,
55+
&PyTuple_Type,
56+
&params,
57+
&PyDict_Type,
58+
&kwargs
59+
)
60+
) return NULL;
61+
int val = setjmp(buf);
3862

63+
if (setjmp(buf)) {
64+
PyErr_Format(
65+
PyExc_RuntimeError,
66+
"segment violation occured during execution of %S",
67+
PyObject_GetAttrString(func, "__name__")
68+
);
69+
return NULL;
70+
}
71+
72+
PyObject* result = PyObject_Call(func, params, kwargs);
73+
return result;
74+
}
3975

4076
static PyMethodDef methods[] = {
4177
{"add_ref", add_ref, METH_VARARGS, "Increment the reference count on the target object."},
4278
{"remove_ref", remove_ref, METH_VARARGS, "Decrement the reference count on the target object."},
4379
{"force_set_attr", force_set_attr, METH_VARARGS, "Force setting an attribute on the target type."},
4480
{"set_ref", set_ref, METH_VARARGS, "Set the reference count on the target object."},
81+
{"handle", handle, METH_VARARGS, "Enable the SIGSEGV handler."},
4582
{NULL, NULL, 0, NULL}
4683
};
4784

@@ -54,5 +91,10 @@ static struct PyModuleDef module = {
5491
};
5592

5693
PyMODINIT_FUNC PyInit__pointers(void) {
94+
if (signal(SIGSEGV, handler) == SIG_ERR) {
95+
PyErr_SetString(PyExc_RuntimeError, "failed to setup SIGSEGV handler");
96+
return NULL;
97+
}
98+
5799
return PyModule_Create(&module);
58100
}

src/pointers/object_pointer.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import ctypes
2-
import struct
32
import sys
43
from typing import TypeVar, Union
54

@@ -42,23 +41,25 @@ def move(
4241

4342
size_a: int = sys.getsizeof(deref_a)
4443
size_b: int = sys.getsizeof(deref_b)
44+
refcnt = sys.getrefcount(deref_b)
4545

4646
if (self._origin_size < size_a) and (not unsafe):
4747
raise InvalidSizeError(
4848
f"target size may not exceed current size ({size_a} < {size_b})", # noqa
4949
)
5050

51+
if type(deref_a) is not type(deref_b):
52+
raise TypeError(
53+
"cannot move object of a different type",
54+
)
55+
5156
current_address: int = self.ensure()
5257
bytes_a = (ctypes.c_ubyte * size_a).from_address(data.ensure())
53-
(refcnt,) = struct.unpack(
54-
"q", ctypes.string_at(current_address, 8)
55-
) # this might be overkill
56-
5758
bytes_b = (ctypes.c_ubyte * size_b).from_address(current_address)
58-
set_ref(deref_a, sys.getrefcount(deref_a) - 1 + refcnt)
5959

6060
self.assign(~data)
6161
ctypes.memmove(bytes_b, bytes_a, len(bytes_a))
62+
set_ref(deref_b, refcnt - 1)
6263

6364
@classmethod
6465
def make_from(cls, obj: Nullable[T]) -> "Pointer[T]":

tests/test_pointer.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import ctypes
2+
13
from ward import raises, test
24

5+
from _pointers import handle
36
from pointers import NULL, InvalidSizeError, Pointer
47
from pointers import _ as m
58
from pointers import strlen, to_c_ptr, to_ptr
@@ -86,3 +89,11 @@ def _():
8689
ptr = m & "test"
8790
assert type(ptr) is Pointer
8891
assert m * ptr == "test"
92+
93+
94+
@test("segfault handler")
95+
def _():
96+
def segfault():
97+
ctypes.string_at(0)
98+
99+
handle(segfault, (), {})

0 commit comments

Comments
 (0)