From 04c3bd91982705adbf4664477c1a004c3bdb1bb9 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Mon, 25 Nov 2024 14:39:49 +0100 Subject: [PATCH 1/5] Make tests pass with GraalPy --- tests/serializers/test_functions.py | 4 ++-- tests/test_errors.py | 3 +-- tests/test_garbage_collection.py | 3 +++ tests/validators/test_dataclasses.py | 1 + tests/validators/test_datetime.py | 4 ++-- tests/validators/test_model_init.py | 1 + tests/validators/test_nullable.py | 1 + tests/validators/test_time.py | 2 +- tests/validators/test_typed_dict.py | 1 + tests/validators/test_with_default.py | 1 + 10 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/serializers/test_functions.py b/tests/serializers/test_functions.py index c31c4e6b6..ec1ced855 100644 --- a/tests/serializers/test_functions.py +++ b/tests/serializers/test_functions.py @@ -661,8 +661,8 @@ def f(value, handler, _info): @pytest.mark.skipif( - platform.python_implementation() == 'PyPy' or sys.platform in {'emscripten', 'win32'}, - reason='fails on pypy, emscripten and windows', + platform.python_implementation() in ('PyPy', 'GraalVM') or sys.platform in {'emscripten', 'win32'}, + reason='fails on pypy, graalpy, emscripten and windows', ) def test_recursive_call(): def bad_recursive(value): diff --git a/tests/test_errors.py b/tests/test_errors.py index 3e52697dd..8a87489d7 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -1155,9 +1155,8 @@ def test_errors_include_url_envvar(env_var, env_var_value, expected_to_have_url) env.pop('PYDANTIC_ERRORS_OMIT_URL', None) # in case the ambient environment has it set if env_var_value is not None: env[env_var] = env_var_value - env['PYTHONDEVMODE'] = '1' # required to surface the deprecation warning result = subprocess.run( - [sys.executable, '-c', code], + [sys.executable, '-W', 'default', '-c', code], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8', diff --git a/tests/test_garbage_collection.py b/tests/test_garbage_collection.py index 3b5bb3c4f..e53cdc564 100644 --- a/tests/test_garbage_collection.py +++ b/tests/test_garbage_collection.py @@ -25,6 +25,7 @@ @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_gc_schema_serializer() -> None: # test for https://github.com/pydantic/pydantic/issues/5136 class BaseModel: @@ -53,6 +54,7 @@ class MyModel(BaseModel): @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_gc_schema_validator() -> None: # test for https://github.com/pydantic/pydantic/issues/5136 class BaseModel: @@ -81,6 +83,7 @@ class MyModel(BaseModel): @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_gc_validator_iterator() -> None: # test for https://github.com/pydantic/pydantic/issues/9243 class MyModel: diff --git a/tests/validators/test_dataclasses.py b/tests/validators/test_dataclasses.py index 6ae9f54f9..93058d4e4 100644 --- a/tests/validators/test_dataclasses.py +++ b/tests/validators/test_dataclasses.py @@ -1519,6 +1519,7 @@ def test_dataclass_wrap_json(): @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') @pytest.mark.parametrize('validator', [None, 'field', 'dataclass']) def test_leak_dataclass(validator): def fn(): diff --git a/tests/validators/test_datetime.py b/tests/validators/test_datetime.py index 5e319dc23..a91a9de52 100644 --- a/tests/validators/test_datetime.py +++ b/tests/validators/test_datetime.py @@ -276,7 +276,7 @@ def tzname(self, _dt): schema.validate_python(dt) # exception messages differ between python and pypy - if platform.python_implementation() == 'PyPy': + if platform.python_implementation() in ('PyPy', 'GraalVM'): error_message = 'NotImplementedError: tzinfo subclass must override utcoffset()' else: error_message = 'NotImplementedError: a tzinfo subclass must implement utcoffset()' @@ -489,7 +489,7 @@ def test_neg_7200(): def test_tz_constraint_too_high(): - with pytest.raises(SchemaError, match='OverflowError: Python int too large to convert to C long'): + with pytest.raises(SchemaError, match='OverflowError: Python int too large.*'): SchemaValidator(core_schema.datetime_schema(tz_constraint=2**64)) diff --git a/tests/validators/test_model_init.py b/tests/validators/test_model_init.py index 463a9a48a..59dc8a15b 100644 --- a/tests/validators/test_model_init.py +++ b/tests/validators/test_model_init.py @@ -414,6 +414,7 @@ def __init__(self, **kwargs): @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') @pytest.mark.parametrize('validator', [None, 'field', 'model']) def test_leak_model(validator): def fn(): diff --git a/tests/validators/test_nullable.py b/tests/validators/test_nullable.py index 779b2507d..af73a8875 100644 --- a/tests/validators/test_nullable.py +++ b/tests/validators/test_nullable.py @@ -42,6 +42,7 @@ def test_union_nullable_bool_int(): @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_leak_nullable(): def fn(): def validate(v, info): diff --git a/tests/validators/test_time.py b/tests/validators/test_time.py index 9a643acfb..78ca0a9e7 100644 --- a/tests/validators/test_time.py +++ b/tests/validators/test_time.py @@ -293,5 +293,5 @@ def test_neg_7200(): def test_tz_constraint_too_high(): - with pytest.raises(SchemaError, match='OverflowError: Python int too large to convert to C long'): + with pytest.raises(SchemaError, match='OverflowError: Python int too large.*'): SchemaValidator(core_schema.time_schema(tz_constraint=2**64)) diff --git a/tests/validators/test_typed_dict.py b/tests/validators/test_typed_dict.py index 3d96d40e0..f7bd47aba 100644 --- a/tests/validators/test_typed_dict.py +++ b/tests/validators/test_typed_dict.py @@ -1143,6 +1143,7 @@ def test_extra_behavior_ignore(config: Union[core_schema.CoreConfig, None], sche @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_leak_typed_dict(): def fn(): def validate(v, info): diff --git a/tests/validators/test_with_default.py b/tests/validators/test_with_default.py index bb8775305..c0b8db8f8 100644 --- a/tests/validators/test_with_default.py +++ b/tests/validators/test_with_default.py @@ -645,6 +645,7 @@ def val_func(v: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Any: @pytest.mark.xfail( condition=platform.python_implementation() == 'PyPy', reason='https://foss.heptapod.net/pypy/pypy/-/issues/3899' ) +@pytest.mark.skipif(platform.python_implementation() == 'GraalVM', reason='Cannot reliably trigger GC on GraalPy') def test_leak_with_default(): def fn(): class Defaulted(int): From 942835e6ebc614796e8922fc23556d7ec90ed476 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Thu, 31 Jul 2025 15:11:53 +0200 Subject: [PATCH 2/5] Add GraalPy to CI tests --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f737b852..9dadefba5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,7 @@ jobs: - '3.14t' - 'pypy3.10' - 'pypy3.11' + - 'graalpy-3.11' runs-on: ubuntu-latest From cf6483662b7107d305aad1dbae348cf96ddee78c Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Thu, 31 Jul 2025 17:38:48 +0200 Subject: [PATCH 3/5] Add classifier for GraalPy --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 202cf41b5..ca3dcddb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: Implementation :: GraalPy', 'Programming Language :: Rust', 'Framework :: Pydantic', 'Intended Audience :: Developers', From ec292199e10ddd81cc7494175bde155735e1b338 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Thu, 31 Jul 2025 16:30:04 +0200 Subject: [PATCH 4/5] Build wheels for GraalPy --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9dadefba5..2a72ed689 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -425,6 +425,10 @@ jobs: manylinux: auto target: x86_64 interpreter: pypy3.10 pypy3.11 + - os: linux + manylinux: auto + target: x86_64 + interpreter: graalpy3.11 # musllinux - os: linux @@ -486,6 +490,7 @@ jobs: args: --release --out dist --interpreter ${{ matrix.interpreter || '3.9 3.10 3.11 3.12 3.13 3.14 pypy3.10 pypy3.11' }} rust-toolchain: stable docker-options: -e CI + before-script-linux: ${{ contains(matrix.interpreter, 'graalpy') && 'manylinux-interpreters ensure-all' || '' }} - run: ${{ (matrix.os == 'windows' && 'dir') || 'ls -lh' }} dist/ From 5e58b1cdc4b1ff85d509e738e8a28cc3b2ad7bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20=C5=A0im=C3=A1=C4=8Dek?= Date: Fri, 1 Aug 2025 13:29:40 +0200 Subject: [PATCH 5/5] Add trove classifiers for all implementations Co-authored-by: Victorien <65306057+Viicos@users.noreply.github.com> --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ca3dcddb1..8cab50714 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,8 @@ classifiers = [ 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Implementation :: GraalPy', 'Programming Language :: Rust', 'Framework :: Pydantic',