From af14567992dfc709a6732ec8830e6840cb83f609 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
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 <vstinner@python.org>
---
 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 <internal/pycore_frame.h>
+#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 <vstinner@python.org>
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 <encukou@gmail.com>
---
 _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 <FrameType>frame.f_back
+    IF PY39B1:
+        f_back = PyFrame_GetBack(frame)
+    ELSE:
+        f_back = frame.f_back
+    if f_back != NULL:
+        return <FrameType>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 <encukou@gmail.com>
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)
