From 90e9169c915a640739880b55ed95f88ce21fa7b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 1 Mar 2022 22:28:40 +0100 Subject: [PATCH] Add Python 3.11 alpha 6 support * On Python 3.11a6 and newer, get the PyFrameObject structure from the internal C API ("internal/pycore_frame.h"). * On Python 3.9 and newer, use PyFrame_GetBack() and PyFrame_GetCode(). * Add frame getter and setter functions to greenlet: * get_f_code(frame) * set_f_lineno(frame, lineno) * set_f_code(frame, code) * greenlet.h: the CFrame type has been renamed to _PyCFrame. Upstream-Status: Submitted [https://github.com/gevent/gevent/pull/1872] Signed-off-by: Alexander Kanavin --- _setuputils.py | 4 +++ deps/greenlet/greenlet.h | 6 +++- src/gevent/_gevent_cgreenlet.pxd | 59 ++++++++++++++++++++++++-------- src/gevent/greenlet.py | 7 ++-- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/_setuputils.py b/_setuputils.py index 7257b3eea..0b14ab1f0 100644 --- a/_setuputils.py +++ b/_setuputils.py @@ -244,6 +244,10 @@ def cythonize1(ext): 'infer_types': True, 'nonecheck': False, }, + compile_time_env={ + 'PY39B1': sys.hexversion >= 0x030900B1, + 'PY311A6': sys.hexversion >= 0x030B00A6, + }, # The common_utility_include_dir (not well documented) # causes Cython to emit separate files for much of the # static support code. Each of the modules then includes diff --git a/deps/greenlet/greenlet.h b/deps/greenlet/greenlet.h index 830bef8dd..f07ce1833 100644 --- a/deps/greenlet/greenlet.h +++ b/deps/greenlet/greenlet.h @@ -14,6 +14,10 @@ extern "C" { /* This is deprecated and undocumented. It does not change. */ #define GREENLET_VERSION "1.0.0" +#if PY_VERSION_HEX < 0x30B00A6 +# define _PyCFrame CFrame +#endif + typedef struct _greenlet { PyObject_HEAD char* stack_start; @@ -39,7 +43,7 @@ typedef struct _greenlet { PyObject* context; #endif #if PY_VERSION_HEX >= 0x30A00B1 - CFrame* cframe; + _PyCFrame* cframe; #endif } PyGreenlet; diff --git a/src/gevent/_gevent_cgreenlet.pxd b/src/gevent/_gevent_cgreenlet.pxd index cbb81a638..246773e24 100644 --- a/src/gevent/_gevent_cgreenlet.pxd +++ b/src/gevent/_gevent_cgreenlet.pxd @@ -57,30 +57,61 @@ cdef extern from "Python.h": ctypedef class types.CodeType [object PyCodeObject]: pass -cdef extern from "frameobject.h": - - ctypedef class types.FrameType [object PyFrameObject]: - cdef CodeType f_code - # Accessing the f_lineno directly doesn't work. There is an accessor - # function, PyFrame_GetLineNumber that is needed to turn the raw line number - # into the executing line number. - # cdef int f_lineno - # We can't declare this in the object as an object, because it's - # allowed to be NULL, and Cython can't handle that. - # We have to go through the python machinery to get a - # proper None instead, or use an inline function. - cdef void* f_back +IF PY311A6: + cdef extern from "internal/pycore_frame.h": + ctypedef class types._PyInterpreterFrame [object _PyInterpreterFrame]: + cdef CodeType f_code + + ctypedef class types.FrameType [object PyFrameObject]: + cdef _PyInterpreterFrame f_frame + # Accessing the f_lineno directly doesn't work. There is an accessor + # function, PyFrame_GetLineNumber that is needed to turn the raw line number + # into the executing line number. + # cdef int f_lineno + # We can't declare this in the object as an object, because it's + # allowed to be NULL, and Cython can't handle that. + # We have to go through the python machinery to get a + # proper None instead, or use an inline function. + cdef void* f_back +ELSE: + cdef extern from "frameobject.h": + ctypedef class types.FrameType [object PyFrameObject]: + cdef CodeType f_code + cdef void* f_back +cdef extern from "frameobject.h": int PyFrame_GetLineNumber(FrameType frame) + IF PY39B1: + CodeType PyFrame_GetCode(FrameType frame) + void* PyFrame_GetBack(FrameType frame) @cython.nonecheck(False) cdef inline FrameType get_f_back(FrameType frame): + IF PY39B1: + f_back = PyFrame_GetBack(frame) + ELSE: + f_back = frame.f_back if frame.f_back != NULL: - return frame.f_back + return f_back cdef inline int get_f_lineno(FrameType frame): return PyFrame_GetLineNumber(frame) +cdef inline void set_f_lineno(FrameType frame, int lineno): + frame.f_lineno = lineno + +cdef inline CodeType get_f_code(FrameType frame): + IF PY39B1: + return PyFrame_GetCode(frame) + ELSE: + return frame.f_code + +cdef inline void set_f_code(FrameType frame, CodeType code): + IF PY311A6: + frame.f_frame.f_code = code + ELSE: + frame.f_code = code + cdef void _init() cdef class SpawnedLink: diff --git a/src/gevent/greenlet.py b/src/gevent/greenlet.py index bed12ed44..f925770bb 100644 --- a/src/gevent/greenlet.py +++ b/src/gevent/greenlet.py @@ -58,6 +58,9 @@ # Frame access locals()['get_f_back'] = lambda frame: frame.f_back locals()['get_f_lineno'] = lambda frame: frame.f_lineno +locals()['set_f_lineno'] = lambda frame, lineno: setattr(frame, 'f_lineno', lineno) +locals()['get_f_code'] = lambda frame: frame.f_code +locals()['set_f_code'] = lambda frame, code: setattr(frame, 'f_code', code) if _PYPY: import _continuation # pylint:disable=import-error @@ -157,8 +160,8 @@ def _extract_stack(limit): # Arguments are always passed to the constructor as Python objects, # meaning we wind up boxing the f_lineno just to unbox it if we pass it. # It's faster to simply assign once the object is created. - older_Frame.f_code = frame.f_code - older_Frame.f_lineno = get_f_lineno(frame) # pylint:disable=undefined-variable + set_f_code(older_Frame.f_code, get_f_code(frame)) + set_f_lineno(older_Frame.f_lineno, get_f_lineno(frame)) # pylint:disable=undefined-variable if newer_Frame is not None: newer_Frame.f_back = older_Frame newer_Frame = older_Frame