diff --git a/.ci/install.sh b/.ci/install.sh index 9553eb8f442..cc104c45f9b 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -39,8 +39,8 @@ python3 -m pip install --only-binary=:all: pyarrow || true # PyQt6 doesn't support PyPy3 if [[ $GHA_PYTHON_VERSION == 3.* ]]; then sudo apt-get -qq install libegl1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0 - # TODO Update condition when pyqt6 supports free-threading - if ! [[ "$PYTHON_GIL" == "0" ]]; then python3 -m pip install pyqt6 ; fi + # pyqt6 doesn't yet support free-threading; only install if a wheel is available + python3 -m pip install --only-binary=:all: pyqt6 || true fi # webp diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d90cc805a9b..362412e9474 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,6 @@ jobs: "3.15", "3.14t", "3.14", - "3.13t", "3.13", "3.12", "3.11", @@ -51,10 +50,6 @@ jobs: include: - { python-version: "3.12", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" } - { python-version: "3.11", PYTHONOPTIMIZE: 2 } - # Free-threaded - - { python-version: "3.15t", disable-gil: true } - - { python-version: "3.14t", disable-gil: true } - - { python-version: "3.13t", disable-gil: true } # Intel - { os: "macos-26-intel", python-version: "3.10" } exclude: @@ -78,11 +73,6 @@ jobs: ".ci/*.sh" "pyproject.toml" - - name: Set PYTHON_GIL - if: "${{ matrix.disable-gil }}" - run: | - echo "PYTHON_GIL=0" >> $GITHUB_ENV - - name: Build system information run: python3 .github/workflows/system-info.py diff --git a/Tests/conftest.py b/Tests/conftest.py index e00d1f019f5..1f32bbedff3 100644 --- a/Tests/conftest.py +++ b/Tests/conftest.py @@ -1,9 +1,17 @@ from __future__ import annotations import io +import sys +import sysconfig import pytest +FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) + +gil_enabled_at_start = True +if FREE_THREADED_BUILD: + gil_enabled_at_start = sys._is_gil_enabled() # type: ignore[attr-defined] + def pytest_report_header(config: pytest.Config) -> str: try: @@ -16,6 +24,25 @@ def pytest_report_header(config: pytest.Config) -> str: return f"pytest_report_header failed: {e}" +def pytest_terminal_summary(terminalreporter: pytest.TerminalReporter) -> None: + if ( + FREE_THREADED_BUILD + and not gil_enabled_at_start + and sys._is_gil_enabled() # type: ignore[attr-defined] + ): + tr = terminalreporter + tr.ensure_newline() + tr.section("GIL re-enabled", red=True, bold=True) + tr.line("The GIL was re-enabled at runtime during the tests.") + tr.line("This can happen with no test failures if the RuntimeWarning") + tr.line("raised by Python when this happens is filtered by a test.") + tr.line("") + tr.line("Please ensure all new C modules declare support for running") + tr.line("without the GIL. Any new tests that intentionally imports") + tr.line("code that re-enables the GIL should do so in a subprocess.") + pytest.exit("GIL re-enabled during tests", returncode=1) + + def pytest_configure(config: pytest.Config) -> None: config.addinivalue_line( "markers",