Skip to content

Commit 31ccde2

Browse files
vstinnerhroncok
andcommitted
Closes #305: Add Python 3.11 support
* Add GREENLET_PY311 macro * PyGreenlet structure: * Add 3 members for the "data stack": 'datastack_chunk', 'datastack_top' and 'datastack_limit'. * Add 'current_frame' member. * Rename CFrame to _PyCFrame * tox.ini: Add py311 environment. Changes partially backport from the master branch: commit 63e1099. Co-Authored-By: Miro Hrončok <[email protected]>
1 parent ea9ad9c commit 31ccde2

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed

src/greenlet/greenlet.c

+48-13
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,11 @@ green_clear_exc(PyGreenlet* g)
170170
{
171171
#if GREENLET_PY37
172172
g->exc_info = NULL;
173-
g->exc_state.exc_type = NULL;
174173
g->exc_state.exc_value = NULL;
174+
#if !GREENLET_PY311
175+
g->exc_state.exc_type = NULL;
175176
g->exc_state.exc_traceback = NULL;
177+
#endif
176178
g->exc_state.previous_item = NULL;
177179
#else
178180
g->exc_type = NULL;
@@ -525,8 +527,13 @@ g_switchstack(void)
525527
{ /* save state */
526528
PyGreenlet* current = ts_current;
527529
PyThreadState* tstate = PyThreadState_GET();
530+
#if GREENLET_PY311
531+
current->recursion_depth = (tstate->recursion_limit
532+
- tstate->recursion_remaining);
533+
#else
528534
current->recursion_depth = tstate->recursion_depth;
529535
current->top_frame = tstate->frame;
536+
#endif
530537
#if GREENLET_PY37
531538
current->context = tstate->context;
532539
#endif
@@ -551,6 +558,15 @@ g_switchstack(void)
551558
*/
552559
current->cframe = tstate->cframe;
553560
ts__g_switchstack_use_tracing = tstate->cframe->use_tracing;
561+
#if GREENLET_PY311
562+
current->current_frame = tstate->cframe->current_frame;
563+
current->datastack_chunk = tstate->datastack_chunk;
564+
current->datastack_top = tstate->datastack_top;
565+
current->datastack_limit = tstate->datastack_limit;
566+
PyFrameObject *frame = PyThreadState_GetFrame(tstate);
567+
Py_XDECREF(frame); /* PyThreadState_GetFrame gives us a new reference. */
568+
current->top_frame = frame;
569+
#endif
554570
#endif
555571
}
556572

@@ -574,9 +590,6 @@ g_switchstack(void)
574590
PyGreenlet* target = ts_target;
575591
PyGreenlet* origin = ts_current;
576592
PyThreadState* tstate = PyThreadState_GET();
577-
tstate->recursion_depth = target->recursion_depth;
578-
tstate->frame = target->top_frame;
579-
target->top_frame = NULL;
580593

581594
#if GREENLET_PY37
582595
tstate->context = target->context;
@@ -607,7 +620,18 @@ g_switchstack(void)
607620
*/
608621
tstate->cframe->use_tracing = ts__g_switchstack_use_tracing;
609622
#endif
610-
623+
#if GREENLET_PY311
624+
tstate->recursion_remaining = (tstate->recursion_limit
625+
- target->recursion_depth);
626+
tstate->cframe->current_frame = target->current_frame;
627+
tstate->datastack_chunk = target->datastack_chunk;
628+
tstate->datastack_top = target->datastack_top;
629+
tstate->datastack_limit = target->datastack_limit;
630+
#else
631+
tstate->recursion_depth = target->recursion_depth;
632+
tstate->frame = target->top_frame;
633+
#endif
634+
target->top_frame = NULL;
611635
assert(ts_origin == NULL);
612636
Py_INCREF(target);
613637
ts_current = target;
@@ -810,7 +834,7 @@ static int GREENLET_NOINLINE(g_initialstub)(void* mark)
810834
We want to defer copying the state info until we're sure
811835
we need it and are in a stable place to do so.
812836
*/
813-
CFrame trace_info;
837+
_PyCFrame trace_info;
814838
#endif
815839
/* save exception in case getattr clears it */
816840
PyErr_Fetch(&exc, &val, &tb);
@@ -875,7 +899,12 @@ static int GREENLET_NOINLINE(g_initialstub)(void* mark)
875899
}
876900
self->top_frame = NULL;
877901
green_clear_exc(self);
902+
#if GREENLET_PY311
903+
self->recursion_depth = (PyThreadState_GET()->recursion_limit
904+
- PyThreadState_GET()->recursion_remaining);
905+
#else
878906
self->recursion_depth = PyThreadState_GET()->recursion_depth;
907+
#endif
879908

880909
/* restore arguments in case they are clobbered */
881910
ts_target = self;
@@ -1006,13 +1035,13 @@ green_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
10061035
it uses the ``root_cframe`` just to have something to put there.
10071036
However, once the greenlet is actually switched to for the first
10081037
time, ``g_initialstub`` (which doesn't actually "return" while the
1009-
greenlet is running) stores a new CFrame on its local stack, and
1038+
greenlet is running) stores a new _PyCFrame on its local stack, and
10101039
copies the appropriate values from the currently running CFrame;
1011-
this is then made the CFrame for the newly-minted greenlet.
1040+
this is then made the _PyCFrame for the newly-minted greenlet.
10121041
``g_initialstub`` then proceeds to call ``glet.run()``, which
1013-
results in ``PyEval_...`` adding the CFrame to the list. Switches
1042+
results in ``PyEval_...`` adding the _PyCFrame to the list. Switches
10141043
continue as normal. Finally, when the greenlet finishes, the call to
1015-
``glet.run()`` returns and the CFrame is taken out of the linked
1044+
``glet.run()`` returns and the _PyCFrame is taken out of the linked
10161045
list and the stack value is now unused and free to expire.
10171046
*/
10181047
((PyGreenlet*)o)->cframe = &PyThreadState_GET()->root_cframe;
@@ -1121,9 +1150,11 @@ green_traverse(PyGreenlet* self, visitproc visit, void* arg)
11211150
Py_VISIT(self->context);
11221151
#endif
11231152
#if GREENLET_PY37
1124-
Py_VISIT(self->exc_state.exc_type);
11251153
Py_VISIT(self->exc_state.exc_value);
1154+
#if !GREENLET_PY311
1155+
Py_VISIT(self->exc_state.exc_type);
11261156
Py_VISIT(self->exc_state.exc_traceback);
1157+
#endif
11271158
#else
11281159
Py_VISIT(self->exc_type);
11291160
Py_VISIT(self->exc_value);
@@ -1159,9 +1190,11 @@ green_clear(PyGreenlet* self)
11591190
Py_CLEAR(self->context);
11601191
#endif
11611192
#if GREENLET_PY37
1162-
Py_CLEAR(self->exc_state.exc_type);
11631193
Py_CLEAR(self->exc_state.exc_value);
1194+
#if !GREENLET_PY311
1195+
Py_CLEAR(self->exc_state.exc_type);
11641196
Py_CLEAR(self->exc_state.exc_traceback);
1197+
#endif
11651198
#else
11661199
Py_CLEAR(self->exc_type);
11671200
Py_CLEAR(self->exc_value);
@@ -1253,9 +1286,11 @@ green_dealloc(PyGreenlet* self)
12531286
Py_CLEAR(self->context);
12541287
#endif
12551288
#if GREENLET_PY37
1256-
Py_CLEAR(self->exc_state.exc_type);
12571289
Py_CLEAR(self->exc_state.exc_value);
1290+
#if !GREENLET_PY311
1291+
Py_CLEAR(self->exc_state.exc_type);
12581292
Py_CLEAR(self->exc_state.exc_traceback);
1293+
#endif
12591294
#else
12601295
Py_CLEAR(self->exc_type);
12611296
Py_CLEAR(self->exc_value);

src/greenlet/greenlet.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ extern "C" {
1414
/* This is deprecated and undocumented. It does not change. */
1515
#define GREENLET_VERSION "1.0.0"
1616

17+
#if PY_VERSION_HEX >= 0x30B00A6
18+
# define GREENLET_PY311 1
19+
/* _PyInterpreterFrame moved to the internal C API in Python 3.11 */
20+
# include <internal/pycore_frame.h>
21+
#else
22+
# define GREENLET_PY311 0
23+
# define _PyCFrame CFrame
24+
#endif
25+
1726
typedef struct _greenlet {
1827
PyObject_HEAD
1928
char* stack_start;
@@ -25,6 +34,12 @@ typedef struct _greenlet {
2534
PyObject* run_info;
2635
struct _frame* top_frame;
2736
int recursion_depth;
37+
#if GREENLET_PY311
38+
_PyInterpreterFrame *current_frame;
39+
_PyStackChunk *datastack_chunk;
40+
PyObject **datastack_top;
41+
PyObject **datastack_limit;
42+
#endif
2843
PyObject* weakreflist;
2944
#if PY_VERSION_HEX >= 0x030700A3
3045
_PyErr_StackItem* exc_info;
@@ -39,7 +54,7 @@ typedef struct _greenlet {
3954
PyObject* context;
4055
#endif
4156
#if PY_VERSION_HEX >= 0x30A00B1
42-
CFrame* cframe;
57+
_PyCFrame* cframe;
4358
#endif
4459
} PyGreenlet;
4560

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
envlist =
3-
py27,py35,py36,py37,py38,py39,py310,docs
3+
py27,py35,py36,py37,py38,py39,py310,py311,docs
44

55
[testenv]
66
commands =

0 commit comments

Comments
 (0)