Skip to content

Add initial support for Python 3.14a7. #442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
python-version: [3.9, "3.10", "3.11", "3.12", "3.13", "3.14.0-alpha.7"]
os: [ubuntu-latest]
include:
# Needs to be all supported Python versions, they upload the built
Expand All @@ -40,6 +40,8 @@ jobs:
python-version: "3.12"
- os: macos-latest
python-version: "3.13"
- os: macos-latest
python-version: "3.14.0-alpha.7"
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand Down
5 changes: 5 additions & 0 deletions src/greenlet/TGreenlet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ using greenlet::refs::BorrowedGreenlet;
# include "internal/pycore_frame.h"
#endif

#if GREENLET_PY314
# include "internal/pycore_interpframe_structs.h"
# include "internal/pycore_interpframe.h"
#endif

// XXX: TODO: Work to remove all virtual functions
// for speed of calling and size of objects (no vtable).
// One pattern is the Curiously Recurring Template
Expand Down
6 changes: 6 additions & 0 deletions src/greenlet/greenlet_cpython_compat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ Greenlet won't compile on anything older than Python 3.11 alpha 4 (see
# define GREENLET_PY313 0
#endif

#if PY_VERSION_HEX >= 0x30E0000
# define GREENLET_PY314 1
#else
# define GREENLET_PY314 0
#endif

#ifndef Py_SET_REFCNT
/* Py_REFCNT and Py_SIZE macros are converted to functions
https://bugs.python.org/issue39573 */
Expand Down
8 changes: 4 additions & 4 deletions src/greenlet/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

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

WIN = sys.platform.startswith("win")
RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS')
Expand Down Expand Up @@ -55,10 +58,7 @@ def __new__(cls, classname, bases, classDict):
return type.__new__(cls, classname, bases, classDict)


class TestCase(TestCaseMetaClass(
"NewBase",
(unittest.TestCase,),
{})):
class TestCase(unittest.TestCase, metaclass=TestCaseMetaClass):

cleanup_attempt_sleep_duration = 0.001
cleanup_max_sleep_seconds = 1
Expand Down
8 changes: 5 additions & 3 deletions src/greenlet/tests/test_contextvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from greenlet import greenlet
from greenlet import getcurrent
from . import TestCase

from . import PY314

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

def test_context_assignment_different_thread(self):
import threading
Expand Down
9 changes: 6 additions & 3 deletions src/greenlet/tests/test_greenlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from . import TestCase
from . import RUNNING_ON_MANYLINUX
from . import PY313
from . import PY314
from .leakcheck import fails_leakcheck


Expand Down Expand Up @@ -123,13 +124,15 @@ def g():
g = RawGreenlet(f)
g.switch()
lst.append('c')

self.assertEqual(sys.getrefcount(g), 2 if not PY314 else 1)
g = RawGreenlet(g)
self.assertEqual(sys.getrefcount(g), 2)
# Python 3.14 elides reference counting operations
# in some cases. See https://github.com/python/cpython/pull/130708
self.assertEqual(sys.getrefcount(g), 2 if not PY314 else 1)
g.switch()
self.assertEqual(lst, ['a', 'b', 'c'])
# Just the one in this frame, plus the one on the stack we pass to the function
self.assertEqual(sys.getrefcount(g), 2)
self.assertEqual(sys.getrefcount(g), 2 if not PY314 else 1)

def test_threads(self):
success = []
Expand Down
8 changes: 6 additions & 2 deletions src/greenlet/tests/test_leaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import greenlet
from . import TestCase
from . import PY314
from .leakcheck import fails_leakcheck
from .leakcheck import ignores_leakcheck
from .leakcheck import RUNNING_ON_MANYLINUX
Expand Down Expand Up @@ -52,12 +53,15 @@ def test_arg_refs(self):

def test_kwarg_refs(self):
kwargs = {}
self.assertEqual(sys.getrefcount(kwargs), 2 if not PY314 else 1)
# pylint:disable=unnecessary-lambda
g = greenlet.greenlet(
lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs))
lambda **gkwargs: greenlet.getcurrent().parent.switch(**gkwargs))
for _ in range(100):
g.switch(**kwargs)
self.assertEqual(sys.getrefcount(kwargs), 2)
# Python 3.14 elides reference counting operations
# in some cases. See https://github.com/python/cpython/pull/130708
self.assertEqual(sys.getrefcount(kwargs), 2 if not PY314 else 1)


@staticmethod
Expand Down
Loading