Skip to content

Commit cbbe9d2

Browse files
committed
Add initial support for Python 3.14a7.
- Header files have moved around. - Reference counting has changed. It appears to be python/cpython#130708 that's eliding some reference counting within functions and caused us to need to lower our expected reference count in a few places. NOTE: I'm not 100% sure this is the case; dis.dis is broken and won't show the function bodies so I can't confirm the new opcodes are being used.
1 parent c2ee55d commit cbbe9d2

File tree

7 files changed

+34
-9
lines changed

7 files changed

+34
-9
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
runs-on: ${{ matrix.os }}
2626
strategy:
2727
matrix:
28-
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
28+
python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14.0-alpha.7"]
2929
os: [ubuntu-latest]
3030
include:
3131
# Needs to be all supported Python versions, they upload the built
@@ -40,6 +40,8 @@ jobs:
4040
python-version: "3.12"
4141
- os: macos-latest
4242
python-version: "3.13"
43+
- os: macos-latest
44+
python-version: "3.14.0-alpha.7"
4345
steps:
4446
- uses: actions/checkout@v4
4547
- name: Set up Python

src/greenlet/TGreenlet.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ using greenlet::refs::BorrowedGreenlet;
2727
# include "internal/pycore_frame.h"
2828
#endif
2929

30+
#if GREENLET_PY314
31+
# include "internal/pycore_interpframe_structs.h"
32+
# include "internal/pycore_interpframe.h"
33+
#endif
34+
3035
// XXX: TODO: Work to remove all virtual functions
3136
// for speed of calling and size of objects (no vtable).
3237
// One pattern is the Curiously Recurring Template

src/greenlet/greenlet_cpython_compat.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ Greenlet won't compile on anything older than Python 3.11 alpha 4 (see
6161
# define GREENLET_PY313 0
6262
#endif
6363

64+
#if PY_VERSION_HEX >= 0x30E0000
65+
# define GREENLET_PY314 1
66+
#else
67+
# define GREENLET_PY314 0
68+
#endif
69+
6470
#ifndef Py_SET_REFCNT
6571
/* Py_REFCNT and Py_SIZE macros are converted to functions
6672
https://bugs.python.org/issue39573 */

src/greenlet/tests/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
PY312 = sys.version_info[:2] >= (3, 12)
2727
PY313 = sys.version_info[:2] >= (3, 13)
28+
# XXX: First tested on 3.14a7. Revisit all uses of this on later versions to ensure they
29+
# are still valid.
30+
PY314 = sys.version_info[:2] >= (3, 14)
2831

2932
WIN = sys.platform.startswith("win")
3033
RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS')

src/greenlet/tests/test_contextvars.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from greenlet import greenlet
1212
from greenlet import getcurrent
1313
from . import TestCase
14-
14+
from . import PY314
1515

1616
try:
1717
from contextvars import Context
@@ -208,8 +208,10 @@ def target():
208208
# Make sure there are no reference leaks
209209
gr = None
210210
gc.collect()
211-
self.assertEqual(sys.getrefcount(old_context), 2)
212-
self.assertEqual(sys.getrefcount(new_context), 2)
211+
# Python 3.14 elides reference counting operations
212+
# in some cases. See https://github.com/python/cpython/pull/130708
213+
self.assertEqual(sys.getrefcount(old_context), 2 if not PY314 else 1)
214+
self.assertEqual(sys.getrefcount(new_context), 2 if not PY314 else 1)
213215

214216
def test_context_assignment_different_thread(self):
215217
import threading

src/greenlet/tests/test_greenlet.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from . import TestCase
1313
from . import RUNNING_ON_MANYLINUX
1414
from . import PY313
15+
from . import PY314
1516
from .leakcheck import fails_leakcheck
1617

1718

@@ -123,13 +124,15 @@ def g():
123124
g = RawGreenlet(f)
124125
g.switch()
125126
lst.append('c')
126-
127+
self.assertEqual(sys.getrefcount(g), 1)
127128
g = RawGreenlet(g)
128-
self.assertEqual(sys.getrefcount(g), 2)
129+
# Python 3.14 elides reference counting operations
130+
# in some cases. See https://github.com/python/cpython/pull/130708
131+
self.assertEqual(sys.getrefcount(g), 2 if not PY314 else 1)
129132
g.switch()
130133
self.assertEqual(lst, ['a', 'b', 'c'])
131134
# Just the one in this frame, plus the one on the stack we pass to the function
132-
self.assertEqual(sys.getrefcount(g), 2)
135+
self.assertEqual(sys.getrefcount(g), 2 if not PY314 else 1)
133136

134137
def test_threads(self):
135138
success = []

src/greenlet/tests/test_leaks.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import greenlet
1616
from . import TestCase
17+
from . import PY314
1718
from .leakcheck import fails_leakcheck
1819
from .leakcheck import ignores_leakcheck
1920
from .leakcheck import RUNNING_ON_MANYLINUX
@@ -52,12 +53,15 @@ def test_arg_refs(self):
5253

5354
def test_kwarg_refs(self):
5455
kwargs = {}
56+
self.assertEqual(sys.getrefcount(kwargs), 1)
5557
# pylint:disable=unnecessary-lambda
5658
g = greenlet.greenlet(
57-
lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs))
59+
lambda **gkwargs: greenlet.getcurrent().parent.switch(**gkwargs))
5860
for _ in range(100):
5961
g.switch(**kwargs)
60-
self.assertEqual(sys.getrefcount(kwargs), 2)
62+
# Python 3.14 elides reference counting operations
63+
# in some cases. See https://github.com/python/cpython/pull/130708
64+
self.assertEqual(sys.getrefcount(kwargs), 2 if not PY314 else 1)
6165

6266

6367
@staticmethod

0 commit comments

Comments
 (0)