From c4a05f934478f4e177665eee8e095af16e6d1af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0im=C3=A1=C4=8Dek?= Date: Mon, 7 Oct 2024 23:12:04 +0200 Subject: [PATCH] Add support for GraalPy (#5380) * Initial support for GraalPy * Mark tests that currently fail on GraalPy with xfail * Add graalpy to CI * Limit test deps on graalpy to available binary wheels * Skip cmake test installed_function on GraalPy CMake won't find libpython on GraalPy, it either fails or silently picks CPython's libpython. * Factor out setting function docstrings into a macro * Try to narrow down skipped tests --- .github/workflows/ci.yml | 5 +++++ include/pybind11/cast.h | 2 +- include/pybind11/detail/common.h | 16 ++++++++++++++- include/pybind11/detail/internals.h | 5 +++-- include/pybind11/detail/type_caster_base.h | 2 +- include/pybind11/eval.h | 8 ++++---- include/pybind11/pybind11.h | 14 ++++++------- include/pybind11/pytypes.h | 4 ++-- tests/conftest.py | 4 ++-- tests/env.py | 1 + tests/requirements.txt | 13 ++++++------ tests/test_buffers.py | 4 ++++ tests/test_call_policies.cpp | 4 ++-- tests/test_call_policies.py | 5 +++++ tests/test_callbacks.cpp | 2 +- tests/test_callbacks.py | 2 ++ tests/test_class.py | 2 +- tests/test_cmake_build/CMakeLists.txt | 16 ++++++++++----- tests/test_cpp_conduit.py | 6 +++++- tests/test_custom_type_setup.py | 4 ++-- tests/test_eigen_matrix.py | 2 ++ tests/test_eigen_tensor.py | 3 +++ tests/test_embed/CMakeLists.txt | 8 +++++--- tests/test_enum.py | 3 +++ tests/test_eval.py | 2 +- tests/test_exceptions.py | 3 +++ tests/test_factory_constructors.py | 13 ++++++++++++ tests/test_gil_scoped.py | 16 +++++++++++++++ tests/test_iostream.py | 6 ++++++ tests/test_kwargs_and_defaults.py | 2 ++ tests/test_methods_and_attributes.py | 8 +++++++- tests/test_modules.py | 7 +++++-- tests/test_multiple_inheritance.py | 24 ++++++++++++++-------- tests/test_numpy_array.py | 3 ++- tests/test_operator_overloading.py | 3 +++ tests/test_pickling.py | 2 +- tests/test_pytypes.py | 8 +++++++- tests/test_sequences_and_iterators.py | 19 ++++++++++++----- tests/test_smart_ptr.py | 12 +++++++++++ tests/test_stl.py | 2 ++ tests/test_type_caster_pyobject_ptr.cpp | 3 ++- tests/test_virtual_functions.py | 9 ++++++-- 42 files changed, 211 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4acbbf5e..4bd01cb7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: - 'pypy-3.8' - 'pypy-3.9' - 'pypy-3.10' + - 'graalpy-24.1' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the @@ -67,6 +68,10 @@ jobs: # Extra ubuntu latest job - runs-on: ubuntu-latest python: '3.11' + exclude: + # The setup-python action currently doesn't have graalpy for windows + - python: 'graalpy-24.1' + runs-on: 'windows-2022' name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index e3c8b9f65..c44ff29d3 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -343,7 +343,7 @@ public: #else // Alternate approach for CPython: this does the same as the above, but optimized // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) { + else if (auto *tp_as_number = Py_TYPE(src.ptr())->tp_as_number) { if (PYBIND11_NB_BOOL(tp_as_number)) { res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); } diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 2a39e88f3..fa4719922 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -299,7 +299,7 @@ PYBIND11_WARNING_DISABLE_MSVC(4505) # define PYBIND11_INTERNAL_NUMPY_1_ONLY_DETECTED #endif -#if defined(PYPY_VERSION) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) +#if (defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) # define PYBIND11_SIMPLE_GIL_MANAGEMENT #endif @@ -387,6 +387,20 @@ PYBIND11_WARNING_POP #define PYBIND11_CONCAT(first, second) first##second #define PYBIND11_ENSURE_INTERNALS_READY pybind11::detail::get_internals(); +#if !defined(GRAALVM_PYTHON) +# define PYBIND11_PYCFUNCTION_GET_DOC(func) ((func)->m_ml->ml_doc) +# define PYBIND11_PYCFUNCTION_SET_DOC(func, doc) \ + do { \ + (func)->m_ml->ml_doc = (doc); \ + } while (0) +#else +# define PYBIND11_PYCFUNCTION_GET_DOC(func) (GraalPyCFunction_GetDoc((PyObject *) (func))) +# define PYBIND11_PYCFUNCTION_SET_DOC(func, doc) \ + do { \ + GraalPyCFunction_SetDoc((PyObject *) (func), (doc)); \ + } while (0) +#endif + #define PYBIND11_CHECK_PYTHON_VERSION \ { \ const char *compiled_ver \ diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index fa224e032..330f5e1cc 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -454,7 +454,7 @@ inline void translate_local_exception(std::exception_ptr p) { inline object get_python_state_dict() { object state_dict; -#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION) +#if PYBIND11_INTERNALS_VERSION <= 4 || defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) state_dict = reinterpret_borrow(PyEval_GetBuiltins()); #else # if PY_VERSION_HEX < 0x03090000 @@ -727,7 +727,8 @@ const char *c_str(Args &&...args) { } inline const char *get_function_record_capsule_name() { -#if PYBIND11_INTERNALS_VERSION > 4 + // On GraalPy, pointer equality of the names is currently not guaranteed +#if PYBIND11_INTERNALS_VERSION > 4 && !defined(GRAALVM_PYTHON) return get_internals().function_record_capsule_name.c_str(); #else return nullptr; diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index e40e44ba6..e7b94aff2 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -459,7 +459,7 @@ PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_i } inline PyThreadState *get_thread_state_unchecked() { -#if defined(PYPY_VERSION) +#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) return PyThreadState_GET(); #elif PY_VERSION_HEX < 0x030D0000 return _PyThreadState_UncheckedGet(); diff --git a/include/pybind11/eval.h b/include/pybind11/eval.h index 74d9b96b8..3ed1b5a4a 100644 --- a/include/pybind11/eval.h +++ b/include/pybind11/eval.h @@ -94,18 +94,18 @@ void exec(const char (&s)[N], object global = globals(), object local = object() eval(s, std::move(global), std::move(local)); } -#if defined(PYPY_VERSION) +#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON) template object eval_file(str, object, object) { - pybind11_fail("eval_file not supported in PyPy3. Use eval"); + pybind11_fail("eval_file not supported in this interpreter. Use eval"); } template object eval_file(str, object) { - pybind11_fail("eval_file not supported in PyPy3. Use eval"); + pybind11_fail("eval_file not supported in this interpreter. Use eval"); } template object eval_file(str) { - pybind11_fail("eval_file not supported in PyPy3. Use eval"); + pybind11_fail("eval_file not supported in this interpreter. Use eval"); } #else template diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index bcdf641f4..2527d25fa 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -573,8 +573,7 @@ protected: // chain. chain_start = rec; rec->next = chain; - auto rec_capsule - = reinterpret_borrow(((PyCFunctionObject *) m_ptr)->m_self); + auto rec_capsule = reinterpret_borrow(PyCFunction_GET_SELF(m_ptr)); rec_capsule.set_pointer(unique_rec.release()); guarded_strdup.release(); } else { @@ -634,12 +633,11 @@ protected: } } - /* Install docstring */ auto *func = (PyCFunctionObject *) m_ptr; - std::free(const_cast(func->m_ml->ml_doc)); // Install docstring if it's non-empty (when at least one option is enabled) - func->m_ml->ml_doc - = signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str()); + auto *doc = signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str()); + std::free(const_cast(PYBIND11_PYCFUNCTION_GET_DOC(func))); + PYBIND11_PYCFUNCTION_SET_DOC(func, doc); if (rec->is_method) { m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); @@ -2780,8 +2778,8 @@ get_type_override(const void *this_ptr, const type_info *this_type, const char * } /* Don't call dispatch code if invoked from overridden function. - Unfortunately this doesn't work on PyPy. */ -#if !defined(PYPY_VERSION) + Unfortunately this doesn't work on PyPy and GraalPy. */ +#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) # if PY_VERSION_HEX >= 0x03090000 PyFrameObject *frame = PyThreadState_GetFrame(PyThreadState_Get()); if (frame != nullptr) { diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 1e76d7bc1..7aafab6dc 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -643,7 +643,7 @@ struct error_fetch_and_normalize { bool have_trace = false; if (m_trace) { -#if !defined(PYPY_VERSION) +#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) auto *tb = reinterpret_cast(m_trace.ptr()); // Get the deepest trace possible. @@ -1356,7 +1356,7 @@ inline bool PyUnicode_Check_Permissive(PyObject *o) { # define PYBIND11_STR_CHECK_FUN PyUnicode_Check #endif -inline bool PyStaticMethod_Check(PyObject *o) { return o->ob_type == &PyStaticMethod_Type; } +inline bool PyStaticMethod_Check(PyObject *o) { return Py_TYPE(o) == &PyStaticMethod_Type; } class kwargs_proxy : public handle { public: diff --git a/tests/conftest.py b/tests/conftest.py index c40b11221..9e7ca8812 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,8 +28,8 @@ except Exception: @pytest.fixture(scope="session", autouse=True) def use_multiprocessing_forkserver_on_linux(): - if sys.platform != "linux": - # The default on Windows and macOS is "spawn": If it's not broken, don't fix it. + if sys.platform != "linux" or sys.implementation.name == "graalpy": + # The default on Windows, macOS and GraalPy is "spawn": If it's not broken, don't fix it. return # Full background: https://github.com/pybind/pybind11/issues/4105#issuecomment-1301004592 diff --git a/tests/env.py b/tests/env.py index 9f5347f2e..b513e455d 100644 --- a/tests/env.py +++ b/tests/env.py @@ -12,6 +12,7 @@ WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") CPYTHON = platform.python_implementation() == "CPython" PYPY = platform.python_implementation() == "PyPy" +GRAALPY = sys.implementation.name == "graalpy" PY_GIL_DISABLED = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) diff --git a/tests/requirements.txt b/tests/requirements.txt index f4f838ec0..688ff6fe8 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,11 +2,12 @@ build~=1.0; python_version>="3.8" numpy~=1.23.0; python_version=="3.8" and platform_python_implementation=="PyPy" numpy~=1.25.0; python_version=="3.9" and platform_python_implementation=='PyPy' -numpy~=1.21.5; platform_python_implementation!="PyPy" and python_version>="3.8" and python_version<"3.10" -numpy~=1.22.2; platform_python_implementation!="PyPy" and python_version=="3.10" -numpy~=1.26.0; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" +numpy~=1.26.0; platform_python_implementation=="GraalVM" and sys_platform=="linux" +numpy~=1.21.5; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version>="3.8" and python_version<"3.10" +numpy~=1.22.2; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version=="3.10" +numpy~=1.26.0; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version>="3.11" and python_version<"3.13" pytest~=7.0 pytest-timeout -scipy~=1.5.4; platform_python_implementation!="PyPy" and python_version<"3.10" -scipy~=1.8.0; platform_python_implementation!="PyPy" and python_version=="3.10" and sys_platform!='win32' -scipy~=1.11.1; platform_python_implementation!="PyPy" and python_version>="3.11" and python_version<"3.13" and sys_platform!='win32' +scipy~=1.5.4; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version<"3.10" +scipy~=1.8.0; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version=="3.10" and sys_platform!='win32' +scipy~=1.11.1; platform_python_implementation!="PyPy" and platform_python_implementation!="GraalVM" and python_version>="3.11" and python_version<"3.13" and sys_platform!='win32' diff --git a/tests/test_buffers.py b/tests/test_buffers.py index 535f33c2d..1aff38ea7 100644 --- a/tests/test_buffers.py +++ b/tests/test_buffers.py @@ -82,6 +82,8 @@ def test_from_python(): for j in range(m4.cols()): assert m3[i, j] == m4[i, j] + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") cstats = ConstructorStats.get(m.Matrix) assert cstats.alive() == 1 del m3, m4 @@ -118,6 +120,8 @@ def test_to_python(): mat2[2, 3] = 5 assert mat2[2, 3] == 5 + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") cstats = ConstructorStats.get(m.Matrix) assert cstats.alive() == 1 del mat diff --git a/tests/test_call_policies.cpp b/tests/test_call_policies.cpp index 92924cb45..9140f7e9f 100644 --- a/tests/test_call_policies.cpp +++ b/tests/test_call_policies.cpp @@ -95,8 +95,8 @@ TEST_SUBMODULE(call_policies, m) { }, py::call_guard()); -#if !defined(PYPY_VERSION) - // `py::call_guard()` should work in PyPy as well, +#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) + // `py::call_guard()` should work in PyPy/GraalPy as well, // but it's unclear how to test it without `PyGILState_GetThisThreadState`. auto report_gil_status = []() { auto is_gil_held = false; diff --git a/tests/test_call_policies.py b/tests/test_call_policies.py index 91670deb3..3b614df45 100644 --- a/tests/test_call_policies.py +++ b/tests/test_call_policies.py @@ -8,6 +8,7 @@ from pybind11_tests import call_policies as m @pytest.mark.xfail("env.PYPY", reason="sometimes comes out 1 off on PyPy", strict=False) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_keep_alive_argument(capture): n_inst = ConstructorStats.detail_reg_inst() with capture: @@ -60,6 +61,7 @@ def test_keep_alive_argument(capture): assert str(excinfo.value) == "Could not activate keep_alive!" +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_keep_alive_return_value(capture): n_inst = ConstructorStats.detail_reg_inst() with capture: @@ -118,6 +120,7 @@ def test_keep_alive_return_value(capture): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 @pytest.mark.xfail("env.PYPY", reason="_PyObject_GetDictPtr is unimplemented") +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_alive_gc(capture): n_inst = ConstructorStats.detail_reg_inst() p = m.ParentGC() @@ -137,6 +140,7 @@ def test_alive_gc(capture): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_alive_gc_derived(capture): class Derived(m.Parent): pass @@ -159,6 +163,7 @@ def test_alive_gc_derived(capture): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_alive_gc_multi_derived(capture): class Derived(m.Parent, m.Child): def __init__(self): diff --git a/tests/test_callbacks.cpp b/tests/test_callbacks.cpp index e303e7656..f28f0a9e2 100644 --- a/tests/test_callbacks.cpp +++ b/tests/test_callbacks.cpp @@ -270,7 +270,7 @@ TEST_SUBMODULE(callbacks, m) { m.add_object("custom_function", PyCFunction_New(custom_def, rec_capsule.ptr())); // This test requires a new ABI version to pass -#if PYBIND11_INTERNALS_VERSION > 4 +#if PYBIND11_INTERNALS_VERSION > 4 && !defined(GRAALVM_PYTHON) // rec_capsule with nullptr name py::capsule rec_capsule2(std::malloc(1), [](void *data) { std::free(data); }); m.add_object("custom_function2", PyCFunction_New(custom_def, rec_capsule2.ptr())); diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index db6d8dece..00b9015d0 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -90,6 +90,7 @@ def test_keyword_args_and_generalized_unpacking(): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_lambda_closure_cleanup(): m.test_lambda_closure_cleanup() cstats = m.payload_cstats() @@ -98,6 +99,7 @@ def test_lambda_closure_cleanup(): assert cstats.move_constructions >= 1 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_cpp_callable_cleanup(): alive_counts = m.test_cpp_callable_cleanup() assert alive_counts == [0, 1, 2, 1, 2, 1, 0] diff --git a/tests/test_class.py b/tests/test_class.py index 9b2b1d834..470d2a326 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -361,7 +361,7 @@ def test_brace_initialization(): assert b.vec == [123, 456] -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY or env.GRAALPY") def test_class_refcount(): """Instances must correctly increase/decrease the reference count of their types (#1029)""" from sys import getrefcount diff --git a/tests/test_cmake_build/CMakeLists.txt b/tests/test_cmake_build/CMakeLists.txt index f28bde08e..ec365c0f6 100644 --- a/tests/test_cmake_build/CMakeLists.txt +++ b/tests/test_cmake_build/CMakeLists.txt @@ -55,8 +55,10 @@ possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) pybind11_add_build_test(subdirectory_function) pybind11_add_build_test(subdirectory_target) -if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") - message(STATUS "Skipping embed test on PyPy") +if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" + OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" + OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy") + message(STATUS "Skipping embed test on PyPy or GraalPy") else() pybind11_add_build_test(subdirectory_embed) endif() @@ -66,10 +68,14 @@ if(PYBIND11_INSTALL) mock_install ${CMAKE_COMMAND} "-DCMAKE_INSTALL_PREFIX=${pybind11_BINARY_DIR}/mock_install" -P "${pybind11_BINARY_DIR}/cmake_install.cmake") - pybind11_add_build_test(installed_function INSTALL) + if(NOT "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy") + pybind11_add_build_test(installed_function INSTALL) + endif() pybind11_add_build_test(installed_target INSTALL) - if(NOT ("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" - )) + if(NOT + ("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" + OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" + OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy")) pybind11_add_build_test(installed_embed INSTALL) endif() endif() diff --git a/tests/test_cpp_conduit.py b/tests/test_cpp_conduit.py index 51fcf6936..eb300587f 100644 --- a/tests/test_cpp_conduit.py +++ b/tests/test_cpp_conduit.py @@ -7,6 +7,7 @@ import exo_planet_pybind11 import home_planet_very_lonely_traveler import pytest +import env from pybind11_tests import cpp_conduit as home_planet @@ -27,7 +28,10 @@ def test_call_cpp_conduit_success(): home_planet.cpp_type_info_capsule_Traveler, b"raw_pointer_ephemeral", ) - assert cap.__class__.__name__ == "PyCapsule" + assert cap.__class__.__name__ == "PyCapsule" or ( + # Note: this will become unnecessary in the next GraalPy release + env.GRAALPY and cap.__class__.__name__ == "capsule" + ) def test_call_cpp_conduit_platform_abi_id_mismatch(): diff --git a/tests/test_custom_type_setup.py b/tests/test_custom_type_setup.py index ca3340bd5..bb2865cad 100644 --- a/tests/test_custom_type_setup.py +++ b/tests/test_custom_type_setup.py @@ -34,7 +34,7 @@ def gc_tester(): # PyPy does not seem to reliably garbage collect. -@pytest.mark.skipif("env.PYPY") +@pytest.mark.skipif("env.PYPY or env.GRAALPY") def test_self_cycle(gc_tester): obj = m.OwnsPythonObjects() obj.value = obj @@ -42,7 +42,7 @@ def test_self_cycle(gc_tester): # PyPy does not seem to reliably garbage collect. -@pytest.mark.skipif("env.PYPY") +@pytest.mark.skipif("env.PYPY or env.GRAALPY") def test_indirect_cycle(gc_tester): obj = m.OwnsPythonObjects() obj_list = [obj] diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index e1d7433f1..7baf99953 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -2,6 +2,7 @@ from __future__ import annotations import pytest +import env # noqa: F401 from pybind11_tests import ConstructorStats np = pytest.importorskip("numpy") @@ -409,6 +410,7 @@ def assert_keeps_alive(cl, method, *args): assert cstats.alive() == start_with +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_eigen_keepalive(): a = m.ReturnTester() cstats = ConstructorStats.get(m.ReturnTester) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index a2b99d9d7..0860c1dad 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -4,6 +4,8 @@ import sys import pytest +import env # noqa: F401 + np = pytest.importorskip("numpy") eigen_tensor = pytest.importorskip("pybind11_tests.eigen_tensor") submodules = [eigen_tensor.c_style, eigen_tensor.f_style] @@ -61,6 +63,7 @@ def assert_equal_tensor_ref(mat, writeable=True, modified=None): @pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("member_name", ["member", "member_view"]) +@pytest.mark.skipif("env.GRAALPY", reason="Different refcounting mechanism") def test_reference_internal(m, member_name): if not hasattr(sys, "getrefcount"): pytest.skip("No reference counting") diff --git a/tests/test_embed/CMakeLists.txt b/tests/test_embed/CMakeLists.txt index 9b539cd42..f646458d1 100644 --- a/tests/test_embed/CMakeLists.txt +++ b/tests/test_embed/CMakeLists.txt @@ -1,8 +1,10 @@ possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) -if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy") - message(STATUS "Skipping embed test on PyPy") - add_custom_target(cpptest) # Dummy target on PyPy. Embedding is not supported. +if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" + OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" + OR "${PYTHON_MODULE_EXTENSION}" MATCHES "graalpy") + message(STATUS "Skipping embed test on PyPy or GraalPy") + add_custom_target(cpptest) # Dummy target on PyPy or GraalPy. Embedding is not supported. set(_suppress_unused_variable_warning "${DOWNLOAD_CATCH}") return() endif() diff --git a/tests/test_enum.py b/tests/test_enum.py index 9914b9001..03cd1c1a6 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -3,9 +3,11 @@ from __future__ import annotations import pytest +import env # noqa: F401 from pybind11_tests import enums as m +@pytest.mark.xfail("env.GRAALPY", reason="TODO should get fixed on GraalPy side") def test_unscoped_enum(): assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne" assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo" @@ -193,6 +195,7 @@ def test_implicit_conversion(): assert repr(x) == "{: 3, : 4}" +@pytest.mark.xfail("env.GRAALPY", reason="TODO should get fixed on GraalPy side") def test_binary_operators(): assert int(m.Flags.Read) == 4 assert int(m.Flags.Write) == 2 diff --git a/tests/test_eval.py b/tests/test_eval.py index 45b68ece7..8ac1907c7 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -19,7 +19,7 @@ def test_evals(capture): assert m.test_eval_failure() -@pytest.mark.xfail("env.PYPY", raises=RuntimeError) +@pytest.mark.xfail("env.PYPY or env.GRAALPY", raises=RuntimeError) def test_eval_file(): filename = os.path.join(os.path.dirname(__file__), "test_eval_call.py") assert m.test_eval_file(filename) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index a8fd105ea..21449d58c 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -201,6 +201,7 @@ def test_custom(msg): assert msg(excinfo.value) == "[PythonMyException7]: abc" +@pytest.mark.xfail("env.GRAALPY", reason="TODO should get fixed on GraalPy side") def test_nested_throws(capture): """Tests nested (e.g. C++ -> Python -> C++) exception handling""" @@ -369,6 +370,7 @@ def _test_flaky_exception_failure_point_init_py_3_12(): "env.PYPY and sys.version_info[:2] < (3, 12)", reason="PyErr_NormalizeException Segmentation fault", ) +@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side") def test_flaky_exception_failure_point_init(): if sys.version_info[:2] < (3, 12): _test_flaky_exception_failure_point_init_before_py_3_12() @@ -376,6 +378,7 @@ def test_flaky_exception_failure_point_init(): _test_flaky_exception_failure_point_init_py_3_12() +@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side") def test_flaky_exception_failure_point_str(): what, py_err_set_after_what = m.error_already_set_what( FlakyException, ("failure_point_str",) diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index 0ddad5e32..1d3a9bcdd 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -4,11 +4,13 @@ import re import pytest +import env # noqa: F401 from pybind11_tests import ConstructorStats from pybind11_tests import factory_constructors as m from pybind11_tests.factory_constructors import tag +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_init_factory_basic(): """Tests py::init_factory() wrapper around various ways of returning the object""" @@ -102,6 +104,7 @@ def test_init_factory_signature(msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_init_factory_casting(): """Tests py::init_factory() wrapper with various upcasting and downcasting returns""" @@ -150,6 +153,7 @@ def test_init_factory_casting(): ] +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_init_factory_alias(): """Tests py::init_factory() wrapper with value conversions and alias types""" @@ -220,6 +224,7 @@ def test_init_factory_alias(): ] +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_init_factory_dual(): """Tests init factory functions with dual main/alias factory functions""" from pybind11_tests.factory_constructors import TestFactory7 @@ -302,6 +307,7 @@ def test_init_factory_dual(): ] +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_no_placement_new(capture): """Prior to 2.2, `py::init<...>` relied on the type supporting placement new; this tests a class without placement new support.""" @@ -350,6 +356,7 @@ def strip_comments(s): return re.sub(r"\s+#.*", "", s) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_a(capture, msg): """When the constructor is overloaded, previous overloads can require a preallocated value. This test makes sure that such preallocated values only happen when they might be necessary, @@ -372,6 +379,7 @@ def test_reallocation_a(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_b(capture, msg): with capture: create_and_destroy(1.5) @@ -388,6 +396,7 @@ def test_reallocation_b(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_c(capture, msg): with capture: create_and_destroy(2, 3) @@ -402,6 +411,7 @@ def test_reallocation_c(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_d(capture, msg): with capture: create_and_destroy(2.5, 3) @@ -417,6 +427,7 @@ def test_reallocation_d(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_e(capture, msg): with capture: create_and_destroy(3.5, 4.5) @@ -432,6 +443,7 @@ def test_reallocation_e(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_f(capture, msg): with capture: create_and_destroy(4, 0.5) @@ -448,6 +460,7 @@ def test_reallocation_f(capture, msg): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_reallocation_g(capture, msg): with capture: create_and_destroy(5, "hi") diff --git a/tests/test_gil_scoped.py b/tests/test_gil_scoped.py index eab92093c..257e66612 100644 --- a/tests/test_gil_scoped.py +++ b/tests/test_gil_scoped.py @@ -211,6 +211,10 @@ def _run_in_threads(test_fn, num_threads, parallel): @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) +@pytest.mark.skipif( + "env.GRAALPY", + reason="GraalPy transiently complains about unfinished threads at process exit", +) def test_run_in_process_one_thread(test_fn): """Makes sure there is no GIL deadlock when running in a thread. @@ -221,6 +225,10 @@ def test_run_in_process_one_thread(test_fn): @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) +@pytest.mark.skipif( + "env.GRAALPY", + reason="GraalPy transiently complains about unfinished threads at process exit", +) def test_run_in_process_multiple_threads_parallel(test_fn): """Makes sure there is no GIL deadlock when running in a thread multiple times in parallel. @@ -231,6 +239,10 @@ def test_run_in_process_multiple_threads_parallel(test_fn): @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) +@pytest.mark.skipif( + "env.GRAALPY", + reason="GraalPy transiently complains about unfinished threads at process exit", +) def test_run_in_process_multiple_threads_sequential(test_fn): """Makes sure there is no GIL deadlock when running in a thread multiple times sequentially. @@ -241,6 +253,10 @@ def test_run_in_process_multiple_threads_sequential(test_fn): @pytest.mark.skipif(sys.platform.startswith("emscripten"), reason="Requires threads") @pytest.mark.parametrize("test_fn", ALL_BASIC_TESTS_PLUS_INTENTIONAL_DEADLOCK) +@pytest.mark.skipif( + "env.GRAALPY", + reason="GraalPy transiently complains about unfinished threads at process exit", +) def test_run_in_process_direct(test_fn): """Makes sure there is no GIL deadlock when using processes. diff --git a/tests/test_iostream.py b/tests/test_iostream.py index c3d987787..606028d6f 100644 --- a/tests/test_iostream.py +++ b/tests/test_iostream.py @@ -6,8 +6,14 @@ from io import StringIO import pytest +import env # noqa: F401 from pybind11_tests import iostream as m +pytestmark = pytest.mark.skipif( + "env.GRAALPY", + reason="Delayed prints from finalizers from other tests can end up in the output", +) + def test_captured(capsys): msg = "I've been redirected to Python, I hope!" diff --git a/tests/test_kwargs_and_defaults.py b/tests/test_kwargs_and_defaults.py index e3f758165..e3e9a0a0d 100644 --- a/tests/test_kwargs_and_defaults.py +++ b/tests/test_kwargs_and_defaults.py @@ -2,6 +2,7 @@ from __future__ import annotations import pytest +import env # noqa: F401 from pybind11_tests import kwargs_and_defaults as m @@ -383,6 +384,7 @@ def test_signatures(): ) +@pytest.mark.skipif("env.GRAALPY", reason="Different refcounting mechanism") def test_args_refcount(): """Issue/PR #1216 - py::args elements get double-inc_ref()ed when combined with regular arguments""" diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index dfa31f546..91c7b7751 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -4,7 +4,7 @@ import sys import pytest -import env # noqa: F401 +import env from pybind11_tests import ConstructorStats from pybind11_tests import methods_and_attributes as m @@ -68,6 +68,9 @@ def test_methods_and_attributes(): instance1.value = 100 assert str(instance1) == "ExampleMandA[value=100]" + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") + cstats = ConstructorStats.get(m.ExampleMandA) assert cstats.alive() == 2 del instance1, instance2 @@ -316,6 +319,8 @@ def test_dynamic_attributes(): instance.__dict__ = [] assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'" + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") cstats = ConstructorStats.get(m.DynamicClass) assert cstats.alive() == 1 del instance @@ -337,6 +342,7 @@ def test_dynamic_attributes(): # https://foss.heptapod.net/pypy/pypy/-/issues/2447 @pytest.mark.xfail("env.PYPY") +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_cyclic_gc(): # One object references itself instance = m.DynamicClass() diff --git a/tests/test_modules.py b/tests/test_modules.py index 95835e14e..436271a70 100644 --- a/tests/test_modules.py +++ b/tests/test_modules.py @@ -39,6 +39,9 @@ def test_reference_internal(): assert str(b.get_a2()) == "A[43]" assert str(b.a2) == "A[43]" + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") + astats, bstats = ConstructorStats.get(ms.A), ConstructorStats.get(ms.B) assert astats.alive() == 2 assert bstats.alive() == 1 @@ -97,9 +100,9 @@ def test_def_submodule_failures(): sm = m.def_submodule(m, b"ScratchSubModuleName") # Using bytes to show it works. assert sm.__name__ == m.__name__ + "." + "ScratchSubModuleName" malformed_utf8 = b"\x80" - if env.PYPY: + if env.PYPY or env.GRAALPY: # It is not worth the effort finding a trigger for a failure when running with PyPy. - pytest.skip("Sufficiently exercised on platforms other than PyPy.") + pytest.skip("Sufficiently exercised on platforms other than PyPy/GraalPy.") else: # Meant to trigger PyModule_GetName() failure: sm_name_orig = sm.__name__ diff --git a/tests/test_multiple_inheritance.py b/tests/test_multiple_inheritance.py index d445824b5..6f5a656f5 100644 --- a/tests/test_multiple_inheritance.py +++ b/tests/test_multiple_inheritance.py @@ -2,7 +2,7 @@ from __future__ import annotations import pytest -import env # noqa: F401 +import env from pybind11_tests import ConstructorStats from pybind11_tests import multiple_inheritance as m @@ -279,8 +279,9 @@ def test_mi_unaligned_base(): c = m.I801C() d = m.I801D() - # + 4 below because we have the two instances, and each instance has offset base I801B2 - assert ConstructorStats.detail_reg_inst() == n_inst + 4 + if not env.GRAALPY: + # + 4 below because we have the two instances, and each instance has offset base I801B2 + assert ConstructorStats.detail_reg_inst() == n_inst + 4 b1c = m.i801b1_c(c) assert b1c is c b2c = m.i801b2_c(c) @@ -290,6 +291,9 @@ def test_mi_unaligned_base(): b2d = m.i801b2_d(d) assert b2d is d + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") + assert ConstructorStats.detail_reg_inst() == n_inst + 4 # no extra instances del c, b1c, b2c assert ConstructorStats.detail_reg_inst() == n_inst + 2 @@ -312,7 +316,8 @@ def test_mi_base_return(): assert d1.a == 1 assert d1.b == 2 - assert ConstructorStats.detail_reg_inst() == n_inst + 4 + if not env.GRAALPY: + assert ConstructorStats.detail_reg_inst() == n_inst + 4 c2 = m.i801c_b2() assert type(c2) is m.I801C @@ -324,12 +329,13 @@ def test_mi_base_return(): assert d2.a == 1 assert d2.b == 2 - assert ConstructorStats.detail_reg_inst() == n_inst + 8 + if not env.GRAALPY: + assert ConstructorStats.detail_reg_inst() == n_inst + 8 - del c2 - assert ConstructorStats.detail_reg_inst() == n_inst + 6 - del c1, d1, d2 - assert ConstructorStats.detail_reg_inst() == n_inst + del c2 + assert ConstructorStats.detail_reg_inst() == n_inst + 6 + del c1, d1, d2 + assert ConstructorStats.detail_reg_inst() == n_inst # Returning an unregistered derived type with a registered base; we won't # pick up the derived type, obviously, but should still work (as an object diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index bc7b3d555..6e8bde826 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -242,6 +242,7 @@ def test_wrap(): assert_references(a1m, a2, a1) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_numpy_view(capture): with capture: ac = m.ArrayClass() @@ -465,7 +466,7 @@ def test_array_resize(): assert b.shape == (8, 8) -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY or env.GRAALPY") def test_array_create_and_resize(): a = m.create_and_resize(2) assert a.size == 4 diff --git a/tests/test_operator_overloading.py b/tests/test_operator_overloading.py index b6760902d..22300eb0f 100644 --- a/tests/test_operator_overloading.py +++ b/tests/test_operator_overloading.py @@ -2,10 +2,12 @@ from __future__ import annotations import pytest +import env # noqa: F401 from pybind11_tests import ConstructorStats from pybind11_tests import operators as m +@pytest.mark.xfail("env.GRAALPY", reason="TODO should get fixed on GraalPy side") def test_operator_overloading(): v1 = m.Vector2(1, 2) v2 = m.Vector(3, -1) @@ -83,6 +85,7 @@ def test_operator_overloading(): assert cstats.move_assignments == 0 +@pytest.mark.xfail("env.GRAALPY", reason="TODO should get fixed on GraalPy side") def test_operators_notimplemented(): """#393: need to return NotSupported to ensure correct arithmetic operator behavior""" diff --git a/tests/test_pickling.py b/tests/test_pickling.py index ad67a1df9..3e2e45396 100644 --- a/tests/test_pickling.py +++ b/tests/test_pickling.py @@ -20,7 +20,7 @@ def test_pickle_simple_callable(): # all C Python versions. with pytest.raises(TypeError) as excinfo: pickle.dumps(m.simple_callable) - assert re.search("can.*t pickle .*PyCapsule.* object", str(excinfo.value)) + assert re.search("can.*t pickle .*[Cc]apsule.* object", str(excinfo.value)) @pytest.mark.parametrize("cls_name", ["Pickleable", "PickleableNew"]) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 6f015eec8..39d0b619b 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -262,6 +262,7 @@ def test_str(doc): m.str_from_std_string_input, ], ) +@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side") def test_surrogate_pairs_unicode_error(func): input_str = "\ud83d\ude4f".encode("utf-8", "surrogatepass") with pytest.raises(UnicodeDecodeError): @@ -420,6 +421,7 @@ def test_accessor_moves(): pytest.skip("Not defined: PYBIND11_HANDLE_REF_DEBUG") +@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side") def test_constructors(): """C++ default and converting constructors are equivalent to type calls in Python""" types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set] @@ -712,6 +714,7 @@ def test_pass_bytes_or_unicode_to_string_types(): m.pass_to_pybind11_str(malformed_utf8) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") @pytest.mark.parametrize( ("create_weakref", "create_weakref_with_callback"), [ @@ -765,7 +768,10 @@ def test_weakref_err(create_weakref, has_callback): ob = C() # Should raise TypeError on CPython - with pytest.raises(TypeError) if not env.PYPY else contextlib.nullcontext(): + cm = pytest.raises(TypeError) + if env.PYPY or env.GRAALPY: + cm = contextlib.nullcontext() + with cm: _ = create_weakref(ob, callback) if has_callback else create_weakref(ob) diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index f609f553d..20abd29d5 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -3,6 +3,7 @@ from __future__ import annotations import pytest from pytest import approx # noqa: PT013 +import env from pybind11_tests import ConstructorStats from pybind11_tests import sequences_and_iterators as m @@ -110,7 +111,8 @@ def test_sequence(): cstats = ConstructorStats.get(m.Sequence) s = m.Sequence(5) - assert cstats.values() == ["of size", "5"] + if not env.GRAALPY: + assert cstats.values() == ["of size", "5"] assert "Sequence" in repr(s) assert len(s) == 5 @@ -123,16 +125,19 @@ def test_sequence(): assert s[3] == approx(56.78, rel=1e-05) rev = reversed(s) - assert cstats.values() == ["of size", "5"] + if not env.GRAALPY: + assert cstats.values() == ["of size", "5"] rev2 = s[::-1] - assert cstats.values() == ["of size", "5"] + if not env.GRAALPY: + assert cstats.values() == ["of size", "5"] it = iter(m.Sequence(0)) for _ in range(3): # __next__ must continue to raise StopIteration with pytest.raises(StopIteration): next(it) - assert cstats.values() == ["of size", "0"] + if not env.GRAALPY: + assert cstats.values() == ["of size", "0"] expected = [0, 56.78, 0, 0, 12.34] assert rev == approx(expected, rel=1e-05) @@ -140,10 +145,14 @@ def test_sequence(): assert rev == rev2 rev[0::2] = m.Sequence([2.0, 2.0, 2.0]) - assert cstats.values() == ["of size", "3", "from std::vector"] + if not env.GRAALPY: + assert cstats.values() == ["of size", "3", "from std::vector"] assert rev == approx([2, 56.78, 2, 0, 2], rel=1e-05) + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") + assert cstats.alive() == 4 del it assert cstats.alive() == 3 diff --git a/tests/test_smart_ptr.py b/tests/test_smart_ptr.py index bf0ae4aeb..9729004ff 100644 --- a/tests/test_smart_ptr.py +++ b/tests/test_smart_ptr.py @@ -2,10 +2,13 @@ from __future__ import annotations import pytest +import env # noqa: F401 + m = pytest.importorskip("pybind11_tests.smart_ptr") from pybind11_tests import ConstructorStats # noqa: E402 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_smart_ptr(capture): # Object1 for i, o in enumerate( @@ -118,6 +121,7 @@ def test_smart_ptr_refcounting(): assert m.test_object1_refcounting() +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_unique_nodelete(): o = m.MyObject4(23) assert o.value == 23 @@ -129,6 +133,7 @@ def test_unique_nodelete(): assert cstats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_unique_nodelete4a(): o = m.MyObject4a(23) assert o.value == 23 @@ -140,6 +145,7 @@ def test_unique_nodelete4a(): assert cstats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_unique_deleter(): m.MyObject4a(0) o = m.MyObject4b(23) @@ -156,6 +162,7 @@ def test_unique_deleter(): assert cstats4b.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_large_holder(): o = m.MyObject5(5) assert o.value == 5 @@ -165,6 +172,7 @@ def test_large_holder(): assert cstats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_shared_ptr_and_references(): s = m.SharedPtrRef() stats = ConstructorStats.get(m.A) @@ -196,6 +204,7 @@ def test_shared_ptr_and_references(): assert stats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_shared_ptr_from_this_and_references(): s = m.SharedFromThisRef() stats = ConstructorStats.get(m.B) @@ -242,6 +251,7 @@ def test_shared_ptr_from_this_and_references(): assert y is z +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_move_only_holder(): a = m.TypeWithMoveOnlyHolder.make() b = m.TypeWithMoveOnlyHolder.make_as_object() @@ -253,6 +263,7 @@ def test_move_only_holder(): assert stats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_holder_with_addressof_operator(): # this test must not throw exception from c++ a = m.TypeForHolderWithAddressOf.make() @@ -283,6 +294,7 @@ def test_holder_with_addressof_operator(): assert stats.alive() == 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_move_only_holder_with_addressof_operator(): a = m.TypeForMoveOnlyHolderWithAddressOf.make() a.print_object() diff --git a/tests/test_stl.py b/tests/test_stl.py index 6f548789e..d1a9ff08b 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -2,6 +2,7 @@ from __future__ import annotations import pytest +import env # noqa: F401 from pybind11_tests import ConstructorStats, UserType from pybind11_tests import stl as m @@ -362,6 +363,7 @@ def test_function_with_string_and_vector_string_arg(): assert m.func_with_string_or_vector_string_arg_overload("A") == 3 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_stl_ownership(): cstats = ConstructorStats.get(m.Placeholder) assert cstats.alive() == 0 diff --git a/tests/test_type_caster_pyobject_ptr.cpp b/tests/test_type_caster_pyobject_ptr.cpp index a45c08b64..758c4ee09 100644 --- a/tests/test_type_caster_pyobject_ptr.cpp +++ b/tests/test_type_caster_pyobject_ptr.cpp @@ -37,7 +37,8 @@ struct WithPyObjectPtrReturnTrampoline : WithPyObjectPtrReturn { std::string call_return_pyobject_ptr(const WithPyObjectPtrReturn *base_class_ptr) { PyObject *returned_obj = base_class_ptr->return_pyobject_ptr(); -#if !defined(PYPY_VERSION) // It is not worth the trouble doing something special for PyPy. +// It is not worth the trouble doing something special for PyPy/GraalPy +#if !defined(PYPY_VERSION) && !defined(GRAALVM_PYTHON) if (Py_REFCNT(returned_obj) != 1) { py::pybind11_fail(__FILE__ ":" PYBIND11_TOSTRING(__LINE__)); } diff --git a/tests/test_virtual_functions.py b/tests/test_virtual_functions.py index bc0177787..617c87b8e 100644 --- a/tests/test_virtual_functions.py +++ b/tests/test_virtual_functions.py @@ -4,7 +4,7 @@ import sys import pytest -import env # noqa: F401 +import env m = pytest.importorskip("pybind11_tests.virtual_functions") from pybind11_tests import ConstructorStats # noqa: E402 @@ -82,6 +82,9 @@ def test_override(capture, msg): """ ) + if env.GRAALPY: + pytest.skip("ConstructorStats is incompatible with GraalPy.") + cstats = ConstructorStats.get(m.ExampleVirt) assert cstats.alive() == 3 del ex12, ex12p, ex12p2 @@ -91,6 +94,7 @@ def test_override(capture, msg): assert cstats.move_constructions >= 0 +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_alias_delay_initialization1(capture): """`A` only initializes its trampoline class when we inherit from it @@ -130,6 +134,7 @@ def test_alias_delay_initialization1(capture): ) +@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") def test_alias_delay_initialization2(capture): """`A2`, unlike the above, is configured to always initialize the alias @@ -188,7 +193,7 @@ def test_alias_delay_initialization2(capture): # PyPy: Reference count > 1 causes call with noncopyable instance # to fail in ncv1.print_nc() -@pytest.mark.xfail("env.PYPY") +@pytest.mark.xfail("env.PYPY or env.GRAALPY") @pytest.mark.skipif( not hasattr(m, "NCVirt"), reason="NCVirt does not work on Intel/PGI/NVCC compilers" )