diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e947689c3..347993e79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,6 +53,8 @@ jobs: tox_env: py312-full - python: '3.13' tox_env: py313-full + - python: '3.13t' + tox_env: py313 - python: '3.14.0-alpha.1 - 3.14' tox_env: py314-full - python: 'pypy-3.10' @@ -67,10 +69,17 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 name: Install Python + if: matrix.python != '3.13t' with: python-version: ${{ matrix.python}} + - name: Install Python + uses: deadsnakes/action@e640ac8743173a67cca4d7d77cd837e514bf98e8 # v3.2.0 + if: matrix.python == '3.13t' + with: + python-version: '3.13' + nogil: true - name: Install apt packages - run: sudo apt-get update && sudo apt-get install libcurl4-openssl-dev + run: sudo apt-get update && sudo apt-get install libcurl4-openssl-dev pkg-config libffi-dev - name: Install tox run: python -m pip install tox -c requirements.txt diff --git a/setup.py b/setup.py index 0b175af85..a860d5747 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ import os import platform import setuptools - +import sysconfig kwargs = {} @@ -35,6 +35,16 @@ platform.python_implementation() == "CPython" and os.environ.get("TORNADO_EXTENSION") != "0" ): + # build with Py_LIMITED_API unless in freethreading build (which does not currently + # support the limited API in py313t) + if not sysconfig.get_config_var("Py_GIL_DISABLED"): + extension_kwargs = dict( + py_limited_api=True, define_macros=[("Py_LIMITED_API", "0x03090000")] + ) + kwargs["options"] = {"bdist_wheel": {"py_limited_api": "cp39"}} + else: + extension_kwargs = {} + # This extension builds and works on pypy as well, although pypy's jit # produces equivalent performance. kwargs["ext_modules"] = [ @@ -45,14 +55,11 @@ # fall back to the pure-python implementation on any build failure. optional=os.environ.get("TORNADO_EXTENSION") != "1", # Use the stable ABI so our wheels are compatible across python - # versions. - py_limited_api=True, - define_macros=[("Py_LIMITED_API", "0x03090000")], + # versions if not in freethreading build + **extension_kwargs, ) ] - kwargs["options"] = {"bdist_wheel": {"py_limited_api": "cp39"}} - setuptools.setup( name="tornado", diff --git a/tornado/test/circlerefs_test.py b/tornado/test/circlerefs_test.py index d5f7e9692..5ae7ccaab 100644 --- a/tornado/test/circlerefs_test.py +++ b/tornado/test/circlerefs_test.py @@ -19,7 +19,7 @@ import tornado from tornado import web, gen, httpclient -from tornado.test.util import skipNotCPython +from tornado.test.util import skipNotCPython, skipFreeThreaded def find_circular_references(garbage): @@ -102,6 +102,7 @@ def assert_no_cycle_garbage(): # GC behavior is cpython-specific @skipNotCPython +@skipFreeThreaded class CircleRefsTest(unittest.TestCase): def test_known_leak(self): # Construct a known leak scenario to make sure the test harness works. diff --git a/tornado/test/util.py b/tornado/test/util.py index 25dd2000f..e205b6b69 100644 --- a/tornado/test/util.py +++ b/tornado/test/util.py @@ -3,6 +3,7 @@ import platform import socket import sys +import sysconfig import textwrap import typing import unittest @@ -23,6 +24,9 @@ skipNotCPython = unittest.skipIf( platform.python_implementation() != "CPython", "Not CPython implementation" ) +skipFreeThreaded = unittest.skipIf( + sysconfig.get_config_var("Py_GIL_DISABLED"), "Freethreaded Python build" +) def _detect_ipv6(): diff --git a/tox.ini b/tox.ini index d80f7275a..75feafc06 100644 --- a/tox.ini +++ b/tox.ini @@ -58,6 +58,8 @@ setenv = # (New in python 3.10, becomes obsolete when utf8 becomes the # default in 3.15) PYTHONWARNDEFAULTENCODING=1 + # force the GIL on + PYTHON_GIL=1 # Allow shell commands in tests allowlist_externals = sh, env