From af14567992dfc709a6732ec8830e6840cb83f609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 8 Jun 2022 15:09:35 +0200 Subject: [PATCH 1/3] Update greenlet for Python 3.11 From https://github.com/python-greenlet/greenlet/pull/306 Co-Authored-By: Victor Stinner --- deps/greenlet/greenlet.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/deps/greenlet/greenlet.h b/deps/greenlet/greenlet.h index 830bef8dd..4a26ace78 100644 --- a/deps/greenlet/greenlet.h +++ b/deps/greenlet/greenlet.h @@ -14,6 +14,15 @@ extern "C" { /* This is deprecated and undocumented. It does not change. */ #define GREENLET_VERSION "1.0.0" +#if PY_VERSION_HEX >= 0x30B00A6 +# define GREENLET_PY311 1 +/* We need this for _PyInterpreterFrame apparently */ +#include +#else +#define GREENLET_PY311 0 +#define _PyCFrame CFrame +#endif + typedef struct _greenlet { PyObject_HEAD char* stack_start; @@ -25,6 +34,12 @@ typedef struct _greenlet { PyObject* run_info; struct _frame* top_frame; int recursion_depth; +#if GREENLET_PY311 + _PyInterpreterFrame *current_frame; + _PyStackChunk *datastack_chunk; + PyObject **datastack_top; + PyObject **datastack_limit; +#endif PyObject* weakreflist; #if PY_VERSION_HEX >= 0x030700A3 _PyErr_StackItem* exc_info; @@ -39,7 +54,7 @@ typedef struct _greenlet { PyObject* context; #endif #if PY_VERSION_HEX >= 0x30A00B1 - CFrame* cframe; + _PyCFrame* cframe; #endif } PyGreenlet; From 4c3c5842bfb2982bf8560821db723b354eeede66 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 1 Mar 2022 22:28:40 +0100 Subject: [PATCH 2/3] Add Python 3.11 beta 3 support Co-Authored-By: Petr Viktorin --- _setuputils.py | 3 ++ src/gevent/_gevent_cgreenlet.pxd | 47 ++++++++++++++++++++------------ src/gevent/greenlet.py | 3 +- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/_setuputils.py b/_setuputils.py index 7257b3eea..62748571e 100644 --- a/_setuputils.py +++ b/_setuputils.py @@ -244,6 +244,9 @@ def cythonize1(ext): 'infer_types': True, 'nonecheck': False, }, + compile_time_env={ + 'PY39B1': sys.hexversion >= 0x030900B1, + }, # 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/src/gevent/_gevent_cgreenlet.pxd b/src/gevent/_gevent_cgreenlet.pxd index cbb81a638..06ca25e5c 100644 --- a/src/gevent/_gevent_cgreenlet.pxd +++ b/src/gevent/_gevent_cgreenlet.pxd @@ -52,35 +52,48 @@ cdef inline void greenlet_init(): PyGreenlet_Import() _greenlet_imported = True -cdef extern from "Python.h": +ctypedef object CodeType - ctypedef class types.CodeType [object PyCodeObject]: - pass +IF PY39B1: + ctypedef object FrameType -cdef extern from "frameobject.h": + cdef extern from "Python.h": + CodeType PyFrame_GetCode(FrameType frame) + void* PyFrame_GetBack(FrameType frame) + +ELSE: + cdef extern from "frameobject.h": + ctypedef class types.FrameType [object PyFrameObject]: + # We don't have PyFrame_GetCode, need to use the pointer directly + cdef CodeType f_code - 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 + # 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 a function. + cdef void* f_back +cdef extern from "frameobject.h": int PyFrame_GetLineNumber(FrameType frame) @cython.nonecheck(False) cdef inline FrameType get_f_back(FrameType frame): - if frame.f_back != NULL: - return frame.f_back + IF PY39B1: + f_back = PyFrame_GetBack(frame) + ELSE: + f_back = frame.f_back + if f_back != NULL: + return f_back cdef inline int get_f_lineno(FrameType frame): return PyFrame_GetLineNumber(frame) +cdef inline CodeType get_f_code(FrameType frame): + IF PY39B1: + return PyFrame_GetCode(frame) + ELSE: + return frame.f_code + cdef void _init() cdef class SpawnedLink: diff --git a/src/gevent/greenlet.py b/src/gevent/greenlet.py index bed12ed44..612a8fd3a 100644 --- a/src/gevent/greenlet.py +++ b/src/gevent/greenlet.py @@ -58,6 +58,7 @@ # Frame access locals()['get_f_back'] = lambda frame: frame.f_back locals()['get_f_lineno'] = lambda frame: frame.f_lineno +locals()['get_f_code'] = lambda frame: frame.f_code if _PYPY: import _continuation # pylint:disable=import-error @@ -157,7 +158,7 @@ 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_code = get_f_code(frame) older_Frame.f_lineno = get_f_lineno(frame) # pylint:disable=undefined-variable if newer_Frame is not None: newer_Frame.f_back = older_Frame From 70ad874dc8d17da81b356d02b9772bee68898f1c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 8 Jun 2022 15:08:45 +0200 Subject: [PATCH 3/3] Amend tests for Python 3.11: No more U file mode, no more inspect.getargspec() --- src/gevent/testing/testcase.py | 15 ++++++++++----- src/gevent/tests/test__fileobject.py | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/gevent/testing/testcase.py b/src/gevent/testing/testcase.py index cd5db8033..e3a9775fc 100644 --- a/src/gevent/testing/testcase.py +++ b/src/gevent/testing/testcase.py @@ -383,6 +383,7 @@ def assert_error(self, kind=None, value=None, error=None, where_type=None): return error def assertMonkeyPatchedFuncSignatures(self, mod_name, func_names=(), exclude=()): + # If inspect.getfullargspec is not available, # We use inspect.getargspec because it's the only thing available # in Python 2.7, but it is deprecated # pylint:disable=deprecated-method,too-many-locals @@ -409,9 +410,13 @@ def assertMonkeyPatchedFuncSignatures(self, mod_name, func_names=(), exclude=()) try: with warnings.catch_warnings(): - warnings.simplefilter("ignore") - gevent_sig = inspect.getargspec(gevent_func) - sig = inspect.getargspec(func) + try: + getfullargspec = inspect.getfullargspec + except AttributeError: + warnings.simplefilter("ignore") + getfullargspec = inspect.getargspec + gevent_sig = getfullargspec(gevent_func) + sig = getfullargspec(func) except TypeError: if funcs_given: raise @@ -420,10 +425,10 @@ def assertMonkeyPatchedFuncSignatures(self, mod_name, func_names=(), exclude=()) # Python 3 can check a lot more than Python 2 can. continue self.assertEqual(sig.args, gevent_sig.args, func_name) - # The next three might not actually matter? + # The next two might not actually matter? self.assertEqual(sig.varargs, gevent_sig.varargs, func_name) - self.assertEqual(sig.keywords, gevent_sig.keywords, func_name) self.assertEqual(sig.defaults, gevent_sig.defaults, func_name) + # Should deal with others: https://docs.python.org/3/library/inspect.html#inspect.getfullargspec def assertEqualFlakyRaceCondition(self, a, b): try: diff --git a/src/gevent/tests/test__fileobject.py b/src/gevent/tests/test__fileobject.py index afe8d7479..1f4e664a0 100644 --- a/src/gevent/tests/test__fileobject.py +++ b/src/gevent/tests/test__fileobject.py @@ -200,6 +200,8 @@ def test_does_not_leak_on_exception(self): @skipUnlessWorksWithRegularFiles def test_rbU_produces_bytes_readline(self): + if sys.version_info > (3, 11): + self.skipTest("U file mode was removed in 3.11") # Including U in rb still produces bytes. # Note that the universal newline behaviour is # essentially ignored in explicit bytes mode. @@ -213,6 +215,8 @@ def test_rbU_produces_bytes_readline(self): @skipUnlessWorksWithRegularFiles def test_rU_produces_native(self): + if sys.version_info > (3, 11): + self.skipTest("U file mode was removed in 3.11") gevent_data = self.__check_native_matches( b'line1\nline2\r\nline3\rlastline\n\n', 'rU', @@ -362,9 +366,15 @@ def test_newlines(self): try: with warnings.catch_warnings(): - warnings.simplefilter('ignore', DeprecationWarning) - # U is deprecated in Python 3, shows up on FileObjectThread - fobj = self._makeOne(r, 'rU') + if sys.version_info > (3, 11): + # U is removed in Python 3.11 + mode = 'r' + self.skipTest("U file mode was removed in 3.11") + else: + # U is deprecated in Python 3, shows up on FileObjectThread + warnings.simplefilter('ignore', DeprecationWarning) + mode = 'rU' + fobj = self._makeOne(r, mode) result = fobj.read() fobj.close() self.assertEqual('line1\nline2\nline3\nline4\nline5\nline6', result)