diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ef6527..6938676 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -99,6 +99,9 @@ jobs: exclude: - os: macos-latest python-version: "pypy-3.11" + include: + - python-version: "3.14t" + os: ubuntu-latest steps: - name: checkout @@ -255,6 +258,9 @@ jobs: exclude: - os: macos-latest python-version: "pypy-3.11" + include: + - python-version: "3.14t" + os: ubuntu-latest steps: - name: checkout @@ -561,7 +567,7 @@ jobs: # PyPy wheels shouldn't be uploaded, remove them if present find dist/ -name "*pypy*" -type f -delete || true find dist/ -name "*none-any*" -type f -delete || true - # Wheels for the no-yet-supported future Python version need to go + # Wheels for the not-yet-supported future Python version need to go find dist/ -name "*3.15*" -type f -delete || true find dist/ -name "*cp315*" -type f -delete || true # For Linux, we only want the manylinux wheels diff --git a/.gitignore b/.gitignore index 07212fd..c9557a8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,6 @@ lib64 log/ parts/ pyvenv.cfg +share/ testing.log var/ diff --git a/.meta.toml b/.meta.toml index f3d6513..7ecdd79 100644 --- a/.meta.toml +++ b/.meta.toml @@ -2,7 +2,7 @@ # https://github.com/zopefoundation/meta/tree/master/src/zope/meta/c-code [meta] template = "c-code" -commit-id = "f62d8bab" +commit-id = "2dc4f53b" [python] with-windows = true @@ -11,6 +11,7 @@ with-sphinx-doctests = false with-future-python = true with-macos = false with-docs = false +with-free-threaded-python = true [coverage] fail-under = 99.5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e120a3d..c1e0027 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: autopep8 args: [--in-place, --aggressive, --aggressive] - repo: https://github.com/asottile/pyupgrade - rev: v3.21.0 + rev: v3.21.2 hooks: - id: pyupgrade args: [--py310-plus] diff --git a/CHANGES.rst b/CHANGES.rst index fd2139c..9ce63bb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,12 @@ Change log 6.3 (unreleased) ---------------- +- Fix compilation on free-threaded Python 3.14t: use ``Py_REFCNT()`` macro + instead of direct ``ob_refcnt`` struct access, guard ``im_self`` mutation + optimization with ``#ifndef Py_GIL_DISABLED``. + +- Add CI testing for free-threaded Python 3.14t (Linux). + 6.2 (2025-11-16) ---------------- diff --git a/src/ExtensionClass/_ExtensionClass.c b/src/ExtensionClass/_ExtensionClass.c index 91585e5..c3893a6 100644 --- a/src/ExtensionClass/_ExtensionClass.c +++ b/src/ExtensionClass/_ExtensionClass.c @@ -884,17 +884,20 @@ PyECMethod_New_(PyObject *callable, PyObject *inst) if (PyMethod_Check(callable)) { - if (callable->ob_refcnt == 1) +#ifndef Py_GIL_DISABLED + if (Py_REFCNT(callable) == 1) { + /* Optimization: reuse the method object when we have exclusive + ownership. Not safe in the free-threaded build because another + thread could observe the mutation of im_self. */ Py_XDECREF(((PyMethodObject*)callable)->im_self); Py_INCREF(inst); ((PyMethodObject*)callable)->im_self = inst; Py_INCREF(callable); return callable; } - else { - return PyMethod_New(PyMethod_GET_FUNCTION(callable), inst); - } +#endif + return PyMethod_New(PyMethod_GET_FUNCTION(callable), inst); } else { return PyMethod_New(callable, inst); diff --git a/tox.ini b/tox.ini index 96c432b..6cd51fa 100644 --- a/tox.ini +++ b/tox.ini @@ -11,6 +11,7 @@ envlist = py313,py313-pure py314,py314-pure py315,py315-pure + py314t,py314t-pure pypy3 coverage @@ -60,7 +61,7 @@ deps = commands_pre = commands = check-manifest - check-python-versions --only pyproject.toml,setup.py,tox.ini,.github/workflows/tests.yml + check-python-versions --only pyproject.toml,setup.py,tox.ini python -m build --sdist --no-isolation twine check dist/*