Skip to content

native-preview: run a test subset on Python 3.11, 3.13, 3.14 and 3.14t - #540

Closed
tae898 wants to merge 1 commit into
native-previewfrom
native-ci-python-versions
Closed

tae898 wants to merge 1 commit into
native-previewfrom
native-ci-python-versions

Conversation

@tae898

@tae898 tae898 commented Oct 25, 2026 •

Copy link
Copy Markdown

Native parity on every supported Python, cheaply. The native wheel is py3-none (a ctypes loader, no per-interpreter extension), so the full suite on 3.12 does not tell whether 3.11, 3.13, 3.14 or the free-threaded 3.14t work. This adds a pyver job to .github/workflows/native-preview.yml that installs the linux-x86_64 wheel of the build job into those four interpreters and runs a small subset of the tests.

What runs

pyver has needs: [version, build], ubuntu-latest, fail-fast: false, a 20 minute timeout and a matrix of 3.11, 3.13, 3.14 and 3.14t. It downloads the existing native-wheel-linux-x86_64 artifact (no rebuild, the very file the test job installs into 3.12) and runs uv run --no-project --python <ver> --with <wheel> ... parity_wheel.py --files <subset>, the same invocation as the test job plus --files. The gate is a copy of the test job's awk (no failure, error, crash or timeout; both copies carry a keep-in-sync comment). Actions are pinned by the SHAs the file already uses. It uploads native-parity-py<ver> (tsv, run log, junit xml).

It is informational: publish does not need it, it is not in REQUIRED_PLATFORMS, and a workflow comment says so. A red leg is visible but never blocks a release. Promote it once it has been green for a while.

One extra step prints sys._is_gil_enabled() after loading the wheel and numpy, pandas, pyarrow and jpype1 into the interpreter, and emits a ::warning:: on the 3.14t leg if the GIL got re-enabled (then that leg would not test free threading).

The subset (12 files, 13 where polars installs)

test_core, test_native_udf (ctypes layer, database lifecycle, callbacks), test_resultset (queries and results), test_parameter_binding, test_type_conversion, test_transaction_config (transactions), test_async_executor, test_concurrency (threads, the part free threading changes most), test_vector, test_numpy_support, test_resultset_arrow, test_columnar_readers (numpy, pandas, arrow), and test_dataframe_ingest (pandas, polars and arrow ingest) on the legs that have polars. I picked files that fail fast on an interpreter-specific problem (ctypes, struct and buffer handling, datetime and decimal conversion, threading, numpy, pandas and arrow C APIs) and are cheap and self-contained. The 70-file suite stays on 3.12 only.

Local results

Run from bindings/python of this branch against the existing native image (ARCADEDB_BACKEND=native, ARCADEDB_NATIVE_LIB=.../nbatch/libarcadedb.so, PYTHONPATH=src, taskset -c 8-15), one pytest process per file, uv run --no-project --python <ver> --with pytest --with numpy --with requests --with jpype1 --with pyarrow --with pandas [--with polars]. Every file passed on every version. Wall time of the whole subset includes the uv start of each file:

Python files passed skipped wall
3.11.15 13 281 32 21 s
3.12.13 (reference) 13 281 32 24 s
3.13.13 13 281 32 21 s
3.14.5 13 281 32 23 s
3.14.5t 12 212 32 19 s

The skips are the NOTYET and NA-JVM categories of tests/conftest.py (tests about Java objects or the JPype bridge), the same ones the 3.12 parity run reports. The 3.12 row is a reference from the wider candidate run; the file list was cut from 22 candidates afterwards.

I also replayed the new job's run steps verbatim (uv invocation, parity_wheel.py --files, the GIL step and the awk gate) against an already-built wheel (arcadedb_embedded_native-26.10.2.dev1-py3-none-manylinux_2_34_x86_64.whl from another worktree, no rebuild) on 3.14t (212 passed, 0 failures, GIL stays off, 30 s) and 3.11 (281 passed, 0 failures, 35 s). In CI the leg adds the uv and dependency downloads (numpy, pandas and pyarrow are about 80 MB), so I expect roughly 3 to 5 minutes per leg, well inside the 10 minute budget.

3.14t decision: included, without polars

Included. All dependencies of the subset have cp314t wheels (numpy 2.5.4, pandas 3.1.0, pyarrow 26.0.0, jpype1 1.7.1, requests, pytest), the wheel and its ctypes layer load and run on the free-threaded build, all 12 files pass, and the GIL stays off after the wheel, numpy, pandas, pyarrow and jpype1 are loaded (sys._is_gil_enabled() is False), so the leg tests free threading for real, including test_concurrency.

The one exception is polars: polars and polars-runtime-32 ship abi3 wheels only, which cannot be installed on a free-threaded build (uv run --no-build --python 3.14t --with polars fails with "Wheels are required for polars-runtime-32"). test_dataframe_ingest.py imports polars at module level (pytest.importorskip), so it would be skipped as a whole file. The 3.14t leg therefore installs no polars and leaves that file out (pandas and arrow ingest and readers stay covered by test_columnar_readers and test_resultset_arrow); the other three legs install polars and run it. This is stated in a workflow comment. If you prefer the strict reading (any missing cp314t wheel means no 3.14t leg), drop the 3.14t matrix entry.

pyproject check

bindings/python/native-wheel/pyproject.toml agrees with the matrix: requires-python = ">=3.11" and the classifiers list 3.11, 3.12, 3.13 and 3.14 (3.12 comes from the test job, the rest from pyver). No change. There is no "Free Threading" trove classifier; I did not add one because it is a support claim (the optional polars extra cannot be installed on 3.14t).

Not verified

The workflow itself cannot be run locally: the artifact download, the 3.14t interpreter download with the pinned uv 0.12.0 (I used uv 0.13.4 locally), the runner core count and the real CI timing are untested until the first native-preview run. actionlint and pre-commit pass on the touched files. Docs: release.md and ci-setup.md mention the new job.

🤖 Generated with Claude Code

The native wheel is py3-none, so the new pyver job reuses the linux-x86_64 wheel of the build job (no rebuild) and installs it into Python 3.11, 3.13, 3.14 and 3.14t (free-threaded). It runs 12 test files (13 where polars installs) through parity_wheel.py --files, with the same gate as the test job. 3.12 stays covered by the full suite in the test job.

The job is informational: publish does not need it and it is not in REQUIRED_PLATFORMS. 3.14t runs without polars (no cp314t wheel) and so without test_dataframe_ingest.py; a step reports whether the GIL stays off.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tae898

tae898 commented Oct 26, 2026

Copy link
Copy Markdown
Author

Closing here: the bindings moved to ArcadeData/arcadedb-python, and this change is merged there as ArcadeData/arcadedb-python#68 (the Python-version job on the built native wheel). The branch stays.

@tae898 tae898 closed this Oct 26, 2026
@tae898
tae898 deleted the native-ci-python-versions branch November 8, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant