Skip to content

Commit c1fd3c9

Browse files
committed
Initial support for GraalPy
1 parent ad9fd39 commit c1fd3c9

33 files changed

+105
-44
lines changed

Diff for: include/pybind11/cast.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ class type_caster<bool> {
343343
#else
344344
// Alternate approach for CPython: this does the same as the above, but optimized
345345
// using the CPython API so as to avoid an unneeded attribute lookup.
346-
else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) {
346+
else if (auto *tp_as_number = Py_TYPE(src.ptr())->tp_as_number) {
347347
if (PYBIND11_NB_BOOL(tp_as_number)) {
348348
res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr());
349349
}

Diff for: include/pybind11/detail/common.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ PYBIND11_WARNING_DISABLE_MSVC(4505)
299299
# define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED
300300
#endif
301301

302-
#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
302+
#if (defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
303303
# define PYBIND11_SIMPLE_GIL_MANAGEMENT
304304
#endif
305305

Diff for: include/pybind11/detail/internals.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ inline void translate_local_exception(std::exception_ptr p) {
454454

455455
inline object get_python_state_dict() {
456456
object state_dict;
457-
#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION)
457+
#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
458458
state_dict = reinterpret_borrow<object>(PyEval_GetBuiltins());
459459
#else
460460
# if PY_VERSION_HEX < 0x03090000

Diff for: include/pybind11/detail/type_caster_base.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i
459459
}
460460

461461
inline PyThreadState *get_thread_state_unchecked() {
462-
#if defined(PYPY_VERSION)
462+
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
463463
return PyThreadState_GET();
464464
#elif PY_VERSION_HEX < 0x030D0000
465465
return _PyThreadState_UncheckedGet();

Diff for: include/pybind11/eval.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,18 @@ void exec(const char (&s)[N], object global = globals(), object local = object()
9494
eval<eval_statements>(s, std::move(global), std::move(local));
9595
}
9696

97-
#if defined(PYPY_VERSION)
97+
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
9898
template <eval_mode mode = eval_statements>
9999
object eval_file(str, object, object) {
100-
pybind11_fail("eval_file not supported in PyPy3. Use eval");
100+
pybind11_fail("eval_file not supported in PyPy3 or GraalPy. Use eval");
101101
}
102102
template <eval_mode mode = eval_statements>
103103
object eval_file(str, object) {
104-
pybind11_fail("eval_file not supported in PyPy3. Use eval");
104+
pybind11_fail("eval_file not supported in PyPy3 or GraalPy. Use eval");
105105
}
106106
template <eval_mode mode = eval_statements>
107107
object eval_file(str) {
108-
pybind11_fail("eval_file not supported in PyPy3. Use eval");
108+
pybind11_fail("eval_file not supported in PyPy3 or GraalPy. Use eval");
109109
}
110110
#else
111111
template <eval_mode mode = eval_statements>

Diff for: include/pybind11/pybind11.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -573,8 +573,7 @@ class cpp_function : public function {
573573
// chain.
574574
chain_start = rec;
575575
rec->next = chain;
576-
auto rec_capsule
577-
= reinterpret_borrow<capsule>(((PyCFunctionObject *) m_ptr)->m_self);
576+
auto rec_capsule = reinterpret_borrow<capsule>(PyCFunction_GetSelf(m_ptr));
578577
rec_capsule.set_pointer(unique_rec.release());
579578
guarded_strdup.release();
580579
} else {
@@ -636,10 +635,16 @@ class cpp_function : public function {
636635

637636
/* Install docstring */
638637
auto *func = (PyCFunctionObject *) m_ptr;
638+
#ifndef GRAALVM_PYTHON
639639
std::free(const_cast<char *>(func->m_ml->ml_doc));
640640
// Install docstring if it's non-empty (when at least one option is enabled)
641641
func->m_ml->ml_doc
642642
= signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str());
643+
#else
644+
std::free(const_cast<char *>(GraalPyCFunction_GetDoc(m_ptr)));
645+
GraalPyCFunction_SetDoc(
646+
m_ptr, signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str()));
647+
#endif
643648

644649
if (rec->is_method) {
645650
m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr());
@@ -2780,8 +2785,8 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char *
27802785
}
27812786

27822787
/* Don't call dispatch code if invoked from overridden function.
2783-
Unfortunately this doesn't work on PyPy. */
2784-
#if !defined(PYPY_VERSION)
2788+
Unfortunately this doesn't work on PyPy and GraalPy. */
2789+
#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON)
27852790
# if PY_VERSION_HEX >= 0x03090000
27862791
PyFrameObject *frame = PyThreadState_GetFrame(PyThreadState_Get());
27872792
if (frame != nullptr) {

Diff for: include/pybind11/pytypes.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ struct error_fetch_and_normalize {
643643

644644
bool have_trace = false;
645645
if (m_trace) {
646-
#if !defined(PYPY_VERSION)
646+
#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON)
647647
auto *tb = reinterpret_cast<PyTracebackObject *>(m_trace.ptr());
648648

649649
// Get the deepest trace possible.
@@ -1356,7 +1356,7 @@ inline bool PyUnicode_Check_Permissive(PyObject *o) {
13561356
# define PYBIND11_STR_CHECK_FUN PyUnicode_Check
13571357
#endif
13581358

1359-
inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; }
1359+
inline bool PyStaticMethod_Check(PyObject *o) { return Py_TYPE(o) == &PyStaticMethod_Type; }
13601360

13611361
class kwargs_proxy : public handle {
13621362
public:

Diff for: tests/conftest.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828

2929
@pytest.fixture(scope="session", autouse=True)
3030
def use_multiprocessing_forkserver_on_linux():
31-
if sys.platform != "linux":
32-
# The default on Windows and macOS is "spawn": If it's not broken, don't fix it.
31+
if sys.platform != "linux" or sys.implementation.name == "graalpy":
32+
# The default on Windows, macOS and GraalPy is "spawn": If it's not broken, don't fix it.
3333
return
3434

3535
# Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592

Diff for: tests/env.py

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
CPYTHON = platform.python_implementation() == "CPython"
1414
PYPY = platform.python_implementation() == "PyPy"
15+
GRAALPY = sys.implementation.name == "graalpy"
1516
PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED"))
1617

1718

Diff for: tests/test_buffers.py

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def test_format_descriptor_format_buffer_info_equiv(cpp_name, np_dtype):
7070
assert not np_array_is_matching
7171

7272

73+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
7374
def test_from_python():
7475
with pytest.raises(RuntimeError) as excinfo:
7576
m.Matrix(np.array([1, 2, 3])) # trying to assign a 1D array
@@ -98,6 +99,7 @@ def test_from_python():
9899
@pytest.mark.xfail(
99100
env.PYPY, reason="PyPy 7.3.7 doesn't clear this anymore", strict=False
100101
)
102+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
101103
def test_to_python():
102104
mat = m.Matrix(5, 4)
103105
assert memoryview(mat).shape == (5, 4)

Diff for: tests/test_call_policies.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ TEST_SUBMODULE(call_policies, m) {
9595
},
9696
py::call_guard<DependentGuard, CustomGuard>());
9797

98-
#if !defined(PYPY_VERSION)
99-
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy as well,
98+
#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON)
99+
// `py::call_guard<py::gil_scoped_release>()` should work in PyPy/GraalPy as well,
100100
// but it's unclear how to test it without `PyGILState_GetThisThreadState`.
101101
auto report_gil_status = []() {
102102
auto is_gil_held = false;

Diff for: tests/test_call_policies.py

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
@pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False)
11+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
1112
def test_keep_alive_argument(capture):
1213
n_inst = ConstructorStats.detail_reg_inst()
1314
with capture:
@@ -60,6 +61,7 @@ def test_keep_alive_argument(capture):
6061
assert str(excinfo.value) == "Could not activate keep_alive!"
6162

6263

64+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
6365
def test_keep_alive_return_value(capture):
6466
n_inst = ConstructorStats.detail_reg_inst()
6567
with capture:
@@ -118,6 +120,7 @@ def test_keep_alive_return_value(capture):
118120

119121
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
120122
@pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented")
123+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
121124
def test_alive_gc(capture):
122125
n_inst = ConstructorStats.detail_reg_inst()
123126
p = m.ParentGC()
@@ -137,6 +140,7 @@ def test_alive_gc(capture):
137140
)
138141

139142

143+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
140144
def test_alive_gc_derived(capture):
141145
class Derived(m.Parent):
142146
pass
@@ -159,6 +163,7 @@ class Derived(m.Parent):
159163
)
160164

161165

166+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
162167
def test_alive_gc_multi_derived(capture):
163168
class Derived(m.Parent, m.Child):
164169
def __init__(self):

Diff for: tests/test_callbacks.py

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def f(*args, **kwargs):
9090
)
9191

9292

93+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
9394
def test_lambda_closure_cleanup():
9495
m.test_lambda_closure_cleanup()
9596
cstats = m.payload_cstats()
@@ -98,6 +99,7 @@ def test_lambda_closure_cleanup():
9899
assert cstats.move_constructions >= 1
99100

100101

102+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
101103
def test_cpp_callable_cleanup():
102104
alive_counts = m.test_cpp_callable_cleanup()
103105
assert alive_counts == [0, 1, 2, 1, 2, 1, 0]

Diff for: tests/test_class.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ def test_brace_initialization():
361361
assert b.vec == [123, 456]
362362

363363

364-
@pytest.mark.xfail("env.PYPY")
364+
@pytest.mark.xfail("env.PYPY or env.GRAALPY")
365365
def test_class_refcount():
366366
"""Instances must correctly increase/decrease the reference count of their types (#1029)"""
367367
from sys import getrefcount

Diff for: tests/test_cmake_build/CMakeLists.txt

+8-4
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID)
5555

5656
pybind11_add_build_test(subdirectory_function)
5757
pybind11_add_build_test(subdirectory_target)
58-
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy")
59-
message(STATUS "Skipping embed test on PyPy")
58+
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy"
59+
OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy"
60+
OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy")
61+
message(STATUS "Skipping embed test on PyPy or GraalPy")
6062
else()
6163
pybind11_add_build_test(subdirectory_embed)
6264
endif()
@@ -68,8 +70,10 @@ if(PYBIND11_INSTALL)
6870

6971
pybind11_add_build_test(installed_function INSTALL)
7072
pybind11_add_build_test(installed_target INSTALL)
71-
if(NOT ("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy"
72-
))
73+
if(NOT
74+
("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy"
75+
OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy"
76+
OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy"))
7377
pybind11_add_build_test(installed_embed INSTALL)
7478
endif()
7579
endif()

Diff for: tests/test_custom_type_setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ def add_ref(obj):
3434

3535

3636
# PyPy does not seem to reliably garbage collect.
37-
@pytest.mark.skipif("env.PYPY")
37+
@pytest.mark.skipif("env.PYPY or env.GRAALPY")
3838
def test_self_cycle(gc_tester):
3939
obj = m.OwnsPythonObjects()
4040
obj.value = obj
4141
gc_tester(obj)
4242

4343

4444
# PyPy does not seem to reliably garbage collect.
45-
@pytest.mark.skipif("env.PYPY")
45+
@pytest.mark.skipif("env.PYPY or env.GRAALPY")
4646
def test_indirect_cycle(gc_tester):
4747
obj = m.OwnsPythonObjects()
4848
obj_list = [obj]

Diff for: tests/test_eigen_matrix.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44

5+
import env # noqa: F401
56
from pybind11_tests import ConstructorStats
67

78
np = pytest.importorskip("numpy")
@@ -409,6 +410,7 @@ def assert_keeps_alive(cl, method, *args):
409410
assert cstats.alive() == start_with
410411

411412

413+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
412414
def test_eigen_keepalive():
413415
a = m.ReturnTester()
414416
cstats = ConstructorStats.get(m.ReturnTester)

Diff for: tests/test_eigen_tensor.py

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import pytest
66

7+
import env # noqa: F401
8+
79
np = pytest.importorskip("numpy")
810
eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor")
911
submodules = [eigen_tensor.c_style, eigen_tensor.f_style]
@@ -61,6 +63,7 @@ def assert_equal_tensor_ref(mat, writeable=True, modified=None):
6163

6264
@pytest.mark.parametrize("m", submodules)
6365
@pytest.mark.parametrize("member_name", ["member", "member_view"])
66+
@pytest.mark.skipif("env.GRAALPY", reason="Different refcounting mechanism")
6467
def test_reference_internal(m, member_name):
6568
if not hasattr(sys, "getrefcount"):
6669
pytest.skip("No reference counting")

Diff for: tests/test_embed/CMakeLists.txt

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID)
22

3-
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy")
4-
message(STATUS "Skipping embed test on PyPy")
5-
add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported.
3+
if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy"
4+
OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy"
5+
OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy")
6+
message(STATUS "Skipping embed test on PyPy or GraalPy")
7+
add_custom_target(cpptest) # Dummy target on PyPy or GraalPy. Embedding is not supported.
68
set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}")
79
return()
810
endif()

Diff for: tests/test_eval.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def test_evals(capture):
1919
assert m.test_eval_failure()
2020

2121

22-
@pytest.mark.xfail("env.PYPY", raises=RuntimeError)
22+
@pytest.mark.xfail("env.PYPY or env.GRAALPY", raises=RuntimeError)
2323
def test_eval_file():
2424
filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py")
2525
assert m.test_eval_file(filename)

Diff for: tests/test_factory_constructors.py

+4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
import pytest
66

7+
import env # noqa: F401
78
from pybind11_tests import ConstructorStats
89
from pybind11_tests import factory_constructors as m
910
from pybind11_tests.factory_constructors import tag
1011

1112

13+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
1214
def test_init_factory_basic():
1315
"""Tests py::init_factory() wrapper around various ways of returning the object"""
1416

@@ -102,6 +104,7 @@ def test_init_factory_signature(msg):
102104
)
103105

104106

107+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
105108
def test_init_factory_casting():
106109
"""Tests py::init_factory() wrapper with various upcasting and downcasting returns"""
107110

@@ -150,6 +153,7 @@ def test_init_factory_casting():
150153
]
151154

152155

156+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
153157
def test_init_factory_alias():
154158
"""Tests py::init_factory() wrapper with value conversions and alias types"""
155159

Diff for: tests/test_kwargs_and_defaults.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44

5+
import env # noqa: F401
56
from pybind11_tests import kwargs_and_defaults as m
67

78

@@ -378,6 +379,7 @@ def test_signatures():
378379
)
379380

380381

382+
@pytest.mark.skipif("env.GRAALPY", reason="Different refcounting mechanism")
381383
def test_args_refcount():
382384
"""Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular
383385
arguments"""

Diff for: tests/test_methods_and_attributes.py

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
)
2020

2121

22+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
2223
def test_methods_and_attributes():
2324
instance1 = m.ExampleMandA()
2425
instance2 = m.ExampleMandA(32)
@@ -295,6 +296,7 @@ def test_property_rvalue_policy():
295296

296297
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
297298
@pytest.mark.xfail("env.PYPY")
299+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
298300
def test_dynamic_attributes():
299301
instance = m.DynamicClass()
300302
assert not hasattr(instance, "foo")
@@ -337,6 +339,7 @@ class PythonDerivedDynamicClass(m.DynamicClass):
337339

338340
# https://foss.heptapod.net/pypy/pypy/-/issues/2447
339341
@pytest.mark.xfail("env.PYPY")
342+
@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
340343
def test_cyclic_gc():
341344
# One object references itself
342345
instance = m.DynamicClass()

0 commit comments

Comments
 (0)