Skip to content

Fix #323: Support Python 3.12 #327

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 13 commits into from
Jun 20, 2023
6 changes: 5 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.5, 3.6, 3.7, 3.8, 3.9, "3.10", "3.11"]
python-version: [3.5, 3.6, 3.7, 3.8, 3.9, "3.10", "3.11", "3.12-dev"]
# Jan 2023: We have pinned back from ubuntu-latest (which is
# now ubuntu 22.04) because older Python versions like
# 3.5, 3.6 and presumably 2.7 are not available in it.
os: [ubuntu-20.04, macos-latest]
exclude:
- os: macos-latest
python-version: 3.12-dev
steps:
- uses: actions/checkout@v3
- name: Set up Python
Expand Down Expand Up @@ -77,6 +80,7 @@ jobs:
path: dist/*whl
- name: Test
run: |
python -VV
python -c 'import greenlet._greenlet as G; assert G.GREENLET_USE_STANDARD_THREADING'
python -m unittest discover -v greenlet.tests
- name: Doctest
Expand Down
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
compilers for some time. See `PR 347
<https://github.com/python-greenlet/greenlet/pull/347>`_ from Khem
Raj.
- Add initial support for Python 3.12. See `issue
<https://github.com/python-greenlet/greenlet/issues/323>`_ and `PR
<https://github.com/python-greenlet/greenlet/pull/327>`_; thanks go
to (at least) Michael Droettboom, Andreas Motl, Thomas A Caswell,
raphaelauv, Hugo van Kemenade, Mark Shannon, and Petr Viktorin.


2.0.2 (2023-01-28)
Expand Down
5 changes: 5 additions & 0 deletions src/greenlet/greenlet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3095,7 +3095,12 @@ static PyObject*
mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module))
{
PyThreadState* tstate = PyThreadState_GET();

#if GREENLET_PY312
return PyLong_FromLong(tstate->trash.delete_nesting);
#else
return PyLong_FromLong(tstate->trash_delete_nesting);
#endif
}

static PyMethodDef GreenMethods[] = {
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 @@ -48,6 +48,12 @@ We have to save and restore this as well.
# define GREENLET_USE_CFRAME 0
#endif

#if PY_VERSION_HEX >= 0x30C0000
# define GREENLET_PY312 1
#else
# define GREENLET_PY312 0
#endif

#if PY_VERSION_HEX >= 0x30B00A4
/*
Greenlet won't compile on anything older than Python 3.11 alpha 4 (see
Expand Down
48 changes: 41 additions & 7 deletions src/greenlet/greenlet_greenlet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ namespace greenlet
_PyCFrame* cframe;
int use_tracing;
#endif
#if GREENLET_PY312
int py_recursion_depth;
int c_recursion_depth;
#else
int recursion_depth;
#endif
int trash_delete_nesting;
#if GREENLET_PY311
_PyInterpreterFrame* current_frame;
Expand Down Expand Up @@ -748,7 +753,12 @@ PythonState::PythonState()
,cframe(nullptr)
,use_tracing(0)
#endif
#if GREENLET_PY312
,py_recursion_depth(0)
,c_recursion_depth(0)
#else
,recursion_depth(0)
#endif
,trash_delete_nesting(0)
#if GREENLET_PY311
,current_frame(nullptr)
Expand Down Expand Up @@ -828,24 +838,34 @@ void PythonState::operator<<(const PyThreadState *const tstate) G_NOEXCEPT
the switch, use `will_switch_from`.
*/
this->cframe = tstate->cframe;
#if !GREENLET_PY312
this->use_tracing = tstate->cframe->use_tracing;
#endif
#endif
#if GREENLET_PY311
#if GREENLET_PY312
this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
this->c_recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining;
#else
this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
#endif
this->current_frame = tstate->cframe->current_frame;
this->datastack_chunk = tstate->datastack_chunk;
this->datastack_top = tstate->datastack_top;
this->datastack_limit = tstate->datastack_limit;
PyFrameObject *frame = PyThreadState_GetFrame((PyThreadState *)tstate);
Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new reference.
this->_top_frame.steal(frame);
#if GREENLET_PY312
this->trash_delete_nesting = tstate->trash.delete_nesting;
#else
this->trash_delete_nesting = tstate->trash_delete_nesting;
#endif
#else
this->recursion_depth = tstate->recursion_depth;
this->_top_frame.steal(tstate->frame);
#endif

// All versions of Python.
this->trash_delete_nesting = tstate->trash_delete_nesting;
#endif
}

void PythonState::operator>>(PyThreadState *const tstate) G_NOEXCEPT
Expand All @@ -864,26 +884,37 @@ void PythonState::operator>>(PyThreadState *const tstate) G_NOEXCEPT
root_cframe here. See note above about why we can't
just copy this from ``origin->cframe->use_tracing``.
*/
#if !GREENLET_PY312
tstate->cframe->use_tracing = this->use_tracing;
#endif
#endif
#if GREENLET_PY311
#if GREENLET_PY312
tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth;
tstate->c_recursion_remaining = C_RECURSION_LIMIT - this->c_recursion_depth;
#else
tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth;
#endif
tstate->cframe->current_frame = this->current_frame;
tstate->datastack_chunk = this->datastack_chunk;
tstate->datastack_top = this->datastack_top;
tstate->datastack_limit = this->datastack_limit;
this->_top_frame.relinquish_ownership();
#if GREENLET_PY312
tstate->trash.delete_nesting = this->trash_delete_nesting;
#else
tstate->trash_delete_nesting = this->trash_delete_nesting;
#endif
#else
tstate->frame = this->_top_frame.relinquish_ownership();
tstate->recursion_depth = this->recursion_depth;
#endif
// All versions of Python.
tstate->trash_delete_nesting = this->trash_delete_nesting;
#endif
}

void PythonState::will_switch_from(PyThreadState *const origin_tstate) G_NOEXCEPT
{
#if GREENLET_USE_CFRAME
#if GREENLET_USE_CFRAME && !GREENLET_PY312
// The weird thing is, we don't actually save this for an
// effect on the current greenlet, it's saved for an
// effect on the target greenlet. That is, we want
Expand All @@ -895,7 +926,10 @@ void PythonState::will_switch_from(PyThreadState *const origin_tstate) G_NOEXCEP
void PythonState::set_initial_state(const PyThreadState* const tstate) G_NOEXCEPT
{
this->_top_frame = nullptr;
#if GREENLET_PY311
#if GREENLET_PY312
this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be C_RECURSION_LIMIT - tstate->c_recursion_remaining?

#elif GREENLET_PY311
this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
#else
this->recursion_depth = tstate->recursion_depth;
Expand Down