Imported Upstream version 0.29.29 upstream/0.29.29
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 13 Jul 2022 06:27:03 +0000 (15:27 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 13 Jul 2022 06:27:03 +0000 (15:27 +0900)
31 files changed:
.gitrev
CHANGES.rst
Cython/Compiler/Builtin.py
Cython/Compiler/Code.py
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/TypeSlots.py
Cython/Shadow.py
Cython/Utility/Coroutine.c
Cython/Utility/CythonFunction.c
Cython/Utility/Exceptions.c
Cython/Utility/ExtensionTypes.c
Cython/Utility/ModuleSetupCode.c
Cython/Utility/ObjectHandling.c
Cython/Utility/Profile.c
Cython/Utility/StringTools.c
PKG-INFO
docs/src/userguide/extension_types.rst
tests/build/depfile_package.srctree
tests/run/builtin_subtype_methods_T653.pyx
tests/run/call_trace_gh4609.srctree [new file with mode: 0644]
tests/run/cpdef_enums.pyx
tests/run/cyfunction.pyx
tests/run/hasattr.pyx
tests/run/modop.pyx
tests/run/reduce_pickle.pyx
tests/run/special_methods_T561.pyx
tests/run/test_unicode.pyx
tests/run/trace_nogil.pyx [new file with mode: 0644]
tests/run/tryexcept.pyx
tests/run/tryfinally.pyx

diff --git a/.gitrev b/.gitrev
index b89ef19226c2a36022630e0d1b98fd03813c42d7..7b302e8490a9c473f4840467d0b66bc9a507552c 100644 (file)
--- a/.gitrev
+++ b/.gitrev
@@ -1 +1 @@
-27b6709241461f620fb25756ef9f1192cc4f589a
+84bf400d038a9bf6e2a7098278c9fa42bd908816
index 720320e79b0502264f25a83a3ab05b34c949cf28..e00490670cff18b5344371d7f275faebdd496ae4 100644 (file)
@@ -2,6 +2,54 @@
 Cython Changelog
 ================
 
+0.29.29 (2022-05-16)
+====================
+
+Features added
+--------------
+
+* Avoid acquiring the GIL at the end of nogil functions.
+  This change was backported in order to avoid generating wrong C code
+  that would trigger C compiler warnings with tracing support enabled.
+  Backport by Oleksandr Pavlyk.  (Github issue #4637)
+
+Bugs fixed
+----------
+
+* Function definitions in ``finally:`` clauses were not correctly generated.
+  Patch by David Woods.  (Github issue #4651)
+
+* A case where C-API functions could be called with a live exception set was fixed.
+  Patch by Jakub Kulík.  (Github issue #4722)
+
+* Pickles can now be exchanged again with those generated from Cython 3.0 modules.
+  (Github issue #4680)
+
+* Cython now correctly generates Python methods for both the provided regular and
+  reversed special numeric methods of extension types.
+  Patch by David Woods.  (Github issue #4750)
+
+* Calling unbound extension type methods without arguments could raise an
+  ``IndexError`` instead of a ``TypeError``.
+  Patch by David Woods.  (Github issue #4779)
+
+* Calling unbound ``.__contains__()`` super class methods on some builtin base
+  types could trigger an infinite recusion.
+  Patch by David Woods.  (Github issue #4785)
+
+* The C union type in pure Python mode mishandled some field names.
+  Patch by Jordan Brière.  (Github issue #4727)
+
+* Allow users to overwrite the C macro ``_USE_MATH_DEFINES``.
+  Patch by Yuriy Chernyshov. (Github issue #4690)
+
+* Improved compatibility with CPython 3.10/11.
+  Patches by Thomas Caswell, David Woods.  (Github issues #4609, #4667, #4721, #4730, #4777)
+
+* Docstrings of descriptors are now provided in PyPy 7.3.9.
+  Patch by Matti Picus.  (Github issue #4701)
+
+
 0.29.28 (2022-02-17)
 ====================
 
index 5fa717507d8e4181639b61dd6599242d35f4018e..e0d203ae0277269b6d27fbef554fb06f4679ff1f 100644 (file)
@@ -271,12 +271,10 @@ builtin_types_table = [
                                     ]),
     ("bytearray", "PyByteArray_Type", [
                                     ]),
-    ("bytes",   "PyBytes_Type",    [BuiltinMethod("__contains__",  "TO",   "b", "PySequence_Contains"),
-                                    BuiltinMethod("join",  "TO",   "O", "__Pyx_PyBytes_Join",
+    ("bytes",   "PyBytes_Type",    [BuiltinMethod("join",  "TO",   "O", "__Pyx_PyBytes_Join",
                                                   utility_code=UtilityCode.load("StringJoin", "StringTools.c")),
                                     ]),
-    ("str",     "PyString_Type",   [BuiltinMethod("__contains__",  "TO",   "b", "PySequence_Contains"),
-                                    BuiltinMethod("join",  "TO",   "O", "__Pyx_PyString_Join",
+    ("str",     "PyString_Type",   [BuiltinMethod("join",  "TO",   "O", "__Pyx_PyString_Join",
                                                   builtin_return_type='basestring',
                                                   utility_code=UtilityCode.load("StringJoin", "StringTools.c")),
                                     ]),
@@ -284,11 +282,9 @@ builtin_types_table = [
                                     BuiltinMethod("join",  "TO",   "T", "PyUnicode_Join"),
                                     ]),
 
-    ("tuple",   "PyTuple_Type",    [BuiltinMethod("__contains__",  "TO",   "b", "PySequence_Contains"),
-                                    ]),
+    ("tuple",   "PyTuple_Type",    []),
 
-    ("list",    "PyList_Type",     [BuiltinMethod("__contains__",  "TO",   "b", "PySequence_Contains"),
-                                    BuiltinMethod("insert",  "TzO",  "r", "PyList_Insert"),
+    ("list",    "PyList_Type",     [BuiltinMethod("insert",  "TzO",  "r", "PyList_Insert"),
                                     BuiltinMethod("reverse", "T",    "r", "PyList_Reverse"),
                                     BuiltinMethod("append",  "TO",   "r", "__Pyx_PyList_Append",
                                                   utility_code=UtilityCode.load("ListAppend", "Optimize.c")),
@@ -326,8 +322,7 @@ builtin_types_table = [
                                     ]),
 #    ("file",    "PyFile_Type",     []),  # not in Py3
 
-    ("set",       "PySet_Type",    [BuiltinMethod("__contains__",  "TO",   "b", "PySequence_Contains"),
-                                    BuiltinMethod("clear",   "T",  "r", "PySet_Clear"),
+    ("set",       "PySet_Type",    [BuiltinMethod("clear",   "T",  "r", "PySet_Clear"),
                                     # discard() and remove() have a special treatment for unhashable values
                                     BuiltinMethod("discard", "TO", "r", "__Pyx_PySet_Discard",
                                                   utility_code=UtilityCode.load("py_set_discard", "Optimize.c")),
index f43c4b2b8e53a7d60aa2962582bf1130474137fc..ed7dceb35ff131df02c5bceff5a6527fbbd4fc43 100644 (file)
@@ -2233,8 +2233,8 @@ class CCodeWriter(object):
         method_flags = entry.signature.method_flags()
         if not method_flags:
             return
-        if entry.is_special:
-            from . import TypeSlots
+        from . import TypeSlots
+        if entry.is_special or TypeSlots.is_reverse_number_slot(entry.name):
             method_flags += [TypeSlots.method_coexist]
         func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
         # Add required casts, but try not to shadow real warnings.
@@ -2407,8 +2407,8 @@ class CCodeWriter(object):
                 UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c"))
         self.putln('__Pyx_RefNannySetupContext("%s", %d);' % (name, acquire_gil and 1 or 0))
 
-    def put_finish_refcount_context(self):
-        self.putln("__Pyx_RefNannyFinishContext();")
+    def put_finish_refcount_context(self, nogil=False):
+        self.putln("__Pyx_RefNannyFinishContextNogil()" if nogil else "__Pyx_RefNannyFinishContext();")
 
     def put_add_traceback(self, qualified_name, include_cline=True):
         """
index aebc5dc0e203807ca3e359adb9c7ce259e0cb66d..01ef96db26fc70bbd1b35462625fa39e74113f46 100644 (file)
@@ -1862,11 +1862,12 @@ class FuncDefNode(StatNode, BlockNode):
 
         use_refnanny = not lenv.nogil or lenv.has_with_gil_block
 
+        gilstate_decl = None
         if acquire_gil or acquire_gil_for_var_decls_only:
             code.put_ensure_gil()
             code.funcstate.gil_owned = True
-        elif lenv.nogil and lenv.has_with_gil_block:
-            code.declare_gilstate()
+        else:
+            gilstate_decl = code.insertion_point()
 
         if profile or linetrace:
             if not self.is_generator:
@@ -1989,6 +1990,19 @@ class FuncDefNode(StatNode, BlockNode):
         code.putln("")
         code.putln("/* function exit code */")
 
+        gil_owned = {
+            'success': code.funcstate.gil_owned,
+            'error': code.funcstate.gil_owned,
+            'gil_state_declared': gilstate_decl is None,
+        }
+        def assure_gil(code_path):
+            if not gil_owned[code_path]:
+                if not gil_owned['gil_state_declared']:
+                    gilstate_decl.declare_gilstate()
+                    gil_owned['gil_state_declared'] = True
+                code.put_ensure_gil(declare_gilstate=False)
+                gil_owned[code_path] = True
+
         # ----- Default return value
         if not self.body.is_terminator:
             if self.return_type.is_pyobject:
@@ -1996,8 +2010,10 @@ class FuncDefNode(StatNode, BlockNode):
                 #    lhs = "(PyObject *)%s" % Naming.retval_cname
                 #else:
                 lhs = Naming.retval_cname
+                assure_gil('success')
                 code.put_init_to_py_none(lhs, self.return_type)
-            else:
+            elif not self.return_type.is_memoryviewslice:
+                # memory view structs receive their default value on initialisation
                 val = self.return_type.default_value
                 if val:
                     code.putln("%s = %s;" % (Naming.retval_cname, val))
@@ -2019,6 +2035,7 @@ class FuncDefNode(StatNode, BlockNode):
                 code.globalstate.use_utility_code(restore_exception_utility_code)
                 code.putln("{ PyObject *__pyx_type, *__pyx_value, *__pyx_tb;")
                 code.putln("__Pyx_PyThreadState_declare")
+                assure_gil('error')
                 code.putln("__Pyx_PyThreadState_assign")
                 code.putln("__Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb);")
                 for entry in used_buffer_entries:
@@ -2038,20 +2055,14 @@ class FuncDefNode(StatNode, BlockNode):
                 # code.globalstate.use_utility_code(get_exception_tuple_utility_code)
                 # code.put_trace_exception()
 
-                if lenv.nogil and not lenv.has_with_gil_block:
-                    code.putln("{")
-                    code.put_ensure_gil()
-
+                assure_gil('error')
                 code.put_add_traceback(self.entry.qualified_name)
-
-                if lenv.nogil and not lenv.has_with_gil_block:
-                    code.put_release_ensured_gil()
-                    code.putln("}")
             else:
                 warning(self.entry.pos,
                         "Unraisable exception in function '%s'." %
                         self.entry.qualified_name, 0)
-                code.put_unraisable(self.entry.qualified_name, lenv.nogil)
+                assure_gil('error')
+                code.put_unraisable(self.entry.qualified_name)
             default_retval = self.return_type.default_value
             if err_val is None and default_retval:
                 err_val = default_retval
@@ -2062,19 +2073,33 @@ class FuncDefNode(StatNode, BlockNode):
                 code.putln("__Pyx_pretend_to_initialize(&%s);" % Naming.retval_cname)
 
             if is_getbuffer_slot:
+                assure_gil('error')
                 self.getbuffer_error_cleanup(code)
 
             # If we are using the non-error cleanup section we should
             # jump past it if we have an error. The if-test below determine
             # whether this section is used.
             if buffers_present or is_getbuffer_slot or self.return_type.is_memoryviewslice:
+                # In the buffer cases, we already called assure_gil('error') and own the GIL.
+                assert gil_owned['error'] or self.return_type.is_memoryviewslice
                 code.put_goto(code.return_from_error_cleanup_label)
+            else:
+                # align error and success GIL state
+                if gil_owned['success']:
+                    assure_gil('error')
+                elif gil_owned['error']:
+                    code.put_release_ensured_gil()
+                    gil_owned['error'] = False
 
         # ----- Non-error return cleanup
         code.put_label(code.return_label)
+        assert gil_owned['error'] == gil_owned['success'], "%s != %s" % (gil_owned['error'], gil_owned['success'])
+
         for entry in used_buffer_entries:
+            assure_gil('success')
             Buffer.put_release_buffer_code(code, entry)
         if is_getbuffer_slot:
+            assure_gil('success')
             self.getbuffer_normal_cleanup(code)
 
         if self.return_type.is_memoryviewslice:
@@ -2084,17 +2109,22 @@ class FuncDefNode(StatNode, BlockNode):
             cond = code.unlikely(self.return_type.error_condition(Naming.retval_cname))
             code.putln(
                 'if (%s) {' % cond)
-            if env.nogil:
+            if not gil_owned['success']:
                 code.put_ensure_gil()
             code.putln(
                 'PyErr_SetString(PyExc_TypeError, "Memoryview return value is not initialized");')
-            if env.nogil:
+            if not gil_owned['success']:
                 code.put_release_ensured_gil()
             code.putln(
                 '}')
 
         # ----- Return cleanup for both error and no-error return
-        code.put_label(code.return_from_error_cleanup_label)
+        if code.label_used(code.return_from_error_cleanup_label):
+            # If we came through the success path, then we took the same GIL decisions as for jumping here.
+            # If we came through the error path and did not jump, then we aligned both paths above.
+            # In the end, all paths are aligned from this point on.
+            assert gil_owned['error'] == gil_owned['success'], "%s != %s" % (gil_owned['error'], gil_owned['success'])
+            code.put_label(code.return_from_error_cleanup_label)
 
         for entry in lenv.var_entries:
             if not entry.used or entry.in_closure:
@@ -2121,6 +2151,7 @@ class FuncDefNode(StatNode, BlockNode):
                 code.put_xdecref_memoryviewslice(entry.cname,
                                                  have_gil=not lenv.nogil)
         if self.needs_closure:
+            assure_gil('success')
             code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
 
         # ----- Return
@@ -2136,6 +2167,7 @@ class FuncDefNode(StatNode, BlockNode):
         if self.entry.is_special and self.entry.name == "__hash__":
             # Returning -1 for __hash__ is supposed to signal an error
             # We do as Python instances and coerce -1 into -2.
+            assure_gil('success')
             code.putln("if (unlikely(%s == -1) && !PyErr_Occurred()) %s = -2;" % (
                 Naming.retval_cname, Naming.retval_cname))
 
@@ -2145,16 +2177,16 @@ class FuncDefNode(StatNode, BlockNode):
                 # generators are traced when iterated, not at creation
                 if self.return_type.is_pyobject:
                     code.put_trace_return(
-                        Naming.retval_cname, nogil=not code.funcstate.gil_owned)
+                        Naming.retval_cname, nogil=not gil_owned['success'])
                 else:
                     code.put_trace_return(
-                        "Py_None", nogil=not code.funcstate.gil_owned)
+                        "Py_None", nogil=not gil_owned['success'])
 
         if not lenv.nogil:
             # GIL holding function
-            code.put_finish_refcount_context()
+            code.put_finish_refcount_context(nogil=not gil_owned['success'])
 
-        if acquire_gil or (lenv.nogil and lenv.has_with_gil_block):
+        if acquire_gil or (lenv.nogil and gil_owned['success']):
             # release the GIL (note that with-gil blocks acquire it on exit in their EnsureGILNode)
             code.put_release_ensured_gil()
             code.funcstate.gil_owned = False
@@ -3461,7 +3493,7 @@ class DefNodeWrapper(FuncDefNode):
                     docstr.as_c_string_literal()))
 
             if entry.is_special:
-                code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
+                code.putln('#if CYTHON_UPDATE_DESCRIPTOR_DOC')
                 code.putln(
                     "struct wrapperbase %s;" % entry.wrapperbase_cname)
                 code.putln('#endif')
@@ -4956,7 +4988,7 @@ class CClassDefNode(ClassDefNode):
                     preprocessor_guard = slot.preprocessor_guard_code() if slot else None
                     if preprocessor_guard:
                         code.putln(preprocessor_guard)
-                    code.putln('#if CYTHON_COMPILING_IN_CPYTHON')
+                    code.putln('#if CYTHON_UPDATE_DESCRIPTOR_DOC')
                     code.putln("{")
                     code.putln(
                         'PyObject *wrapper = PyObject_GetAttrString((PyObject *)&%s, "%s"); %s' % (
@@ -7702,6 +7734,8 @@ class TryFinallyStatNode(StatNode):
     def generate_function_definitions(self, env, code):
         self.body.generate_function_definitions(env, code)
         self.finally_clause.generate_function_definitions(env, code)
+        if self.finally_except_clause:
+            self.finally_except_clause.generate_function_definitions(env, code)
 
     def put_error_catcher(self, code, temps_to_clean_up, exc_vars,
                           exc_lineno_cnames=None, exc_filename_cname=None):
index 0da3670caee56284fc7847c201a1169f8d3ee90a..24f475120422bfdb37d574562590c4a2a72db425 100644 (file)
@@ -1701,7 +1701,23 @@ if VALUE is not None:
                     e.type.create_to_py_utility_code(env)
                     e.type.create_from_py_utility_code(env)
             all_members_names = sorted([e.name for e in all_members])
-            checksum = '0x%s' % hashlib.md5(' '.join(all_members_names).encode('utf-8')).hexdigest()[:7]
+
+            # Cython 0.x used MD5 for the checksum, which a few Python installations remove for security reasons.
+            # SHA-256 should be ok for years to come, but early Cython 3.0 alpha releases used SHA-1,
+            # which may not be.
+            checksum_algos = []
+            try:
+                checksum_algos.append(hashlib.md5)
+            except AttributeError:
+                pass
+            checksum_algos.append(hashlib.sha256)
+            checksum_algos.append(hashlib.sha1)
+
+            member_names_string = ' '.join(all_members_names).encode('utf-8')
+            checksums = [
+                '0x' + mkchecksum(member_names_string).hexdigest()[:7]
+                for mkchecksum in checksum_algos
+            ]
             unpickle_func_name = '__pyx_unpickle_%s' % node.class_name
 
             # TODO(robertwb): Move the state into the third argument
@@ -1710,9 +1726,9 @@ if VALUE is not None:
                 def %(unpickle_func_name)s(__pyx_type, long __pyx_checksum, __pyx_state):
                     cdef object __pyx_PickleError
                     cdef object __pyx_result
-                    if __pyx_checksum != %(checksum)s:
+                    if __pyx_checksum not in %(checksums)s:
                         from pickle import PickleError as __pyx_PickleError
-                        raise __pyx_PickleError("Incompatible checksums (%%s vs %(checksum)s = (%(members)s))" %% __pyx_checksum)
+                        raise __pyx_PickleError("Incompatible checksums (0x%%x vs %(checksums)s = (%(members)s))" %% __pyx_checksum)
                     __pyx_result = %(class_name)s.__new__(__pyx_type)
                     if __pyx_state is not None:
                         %(unpickle_func_name)s__set_state(<%(class_name)s> __pyx_result, __pyx_state)
@@ -1724,7 +1740,7 @@ if VALUE is not None:
                         __pyx_result.__dict__.update(__pyx_state[%(num_members)d])
                 """ % {
                     'unpickle_func_name': unpickle_func_name,
-                    'checksum': checksum,
+                    'checksums': "(%s)" % ', '.join(checksums),
                     'members': ', '.join(all_members_names),
                     'class_name': node.class_name,
                     'assignments': '; '.join(
@@ -1757,7 +1773,7 @@ if VALUE is not None:
                     %(unpickle_func_name)s__set_state(self, __pyx_state)
                 """ % {
                     'unpickle_func_name': unpickle_func_name,
-                    'checksum': checksum,
+                    'checksum': checksums[0],
                     'members': ', '.join('self.%s' % v for v in all_members_names) + (',' if len(all_members_names) == 1 else ''),
                     # Even better, we could check PyType_IS_GC.
                     'any_notnone_members' : ' or '.join(['self.%s is not None' % e.name for e in all_members if e.type.is_pyobject] or ['False']),
@@ -1843,19 +1859,6 @@ if VALUE is not None:
 
         return node
 
-    def _handle_nogil_cleanup(self, lenv, node):
-        "Handle cleanup for 'with gil' blocks in nogil functions."
-        if lenv.nogil and lenv.has_with_gil_block:
-            # Acquire the GIL for cleanup in 'nogil' functions, by wrapping
-            # the entire function body in try/finally.
-            # The corresponding release will be taken care of by
-            # Nodes.FuncDefNode.generate_function_definitions()
-            node.body = Nodes.NogilTryFinallyStatNode(
-                node.body.pos,
-                body=node.body,
-                finally_clause=Nodes.EnsureGILNode(node.body.pos),
-                finally_except_clause=Nodes.EnsureGILNode(node.body.pos))
-
     def _handle_fused(self, node):
         if node.is_generator and node.has_fused_arguments:
             node.has_fused_arguments = False
@@ -1896,7 +1899,6 @@ if VALUE is not None:
             node = self._create_fused_function(env, node)
         else:
             node.body.analyse_declarations(lenv)
-            self._handle_nogil_cleanup(lenv, node)
             self._super_visit_FuncDefNode(node)
 
         self.seen_vars_stack.pop()
index 0b4ff670425c5e5515b0437aba4091242c1226ed..c6867447d256757b6101a0be2cd7fc8874707118 100644 (file)
@@ -625,6 +625,20 @@ def get_slot_code_by_name(scope, slot_name):
     slot = get_slot_by_name(slot_name)
     return slot.slot_code(scope)
 
+def is_reverse_number_slot(name):
+    """
+    Tries to identify __radd__ and friends (so the METH_COEXIST flag can be applied).
+
+    There's no great consequence if it inadvertently identifies a few other methods
+    so just use a simple rule rather than an exact list.
+    """
+    if name.startswith("__r") and name.endswith("__"):
+        forward_name = name.replace("r", "", 1)
+        for meth in PyNumberMethods:
+            if getattr(meth, "method_name", None) == forward_name:
+                return True
+    return False
+
 
 #------------------------------------------------------------------------------------------
 #
index fb662dc05c10df2f3a434bb05a9b617d522b6c8d..cfbd7f46b873ca838cc67c7a0c2b348c45c260b2 100644 (file)
@@ -1,7 +1,7 @@
 # cython.* namespace for pure mode.
 from __future__ import absolute_import
 
-__version__ = "0.29.28"
+__version__ = "0.29.29"
 
 try:
     from __builtin__ import basestring
@@ -305,7 +305,7 @@ class UnionType(CythonType):
             setattr(self, key, value)
 
     def __setattr__(self, key, value):
-        if key in '__dict__':
+        if key == '__dict__':
             CythonType.__setattr__(self, key, value)
         elif key in self._members:
             self.__dict__ = {key: cast(self._members[key], value)}
index f1bb83cb547867cb30de34b191729083bb1cf130..1ad27df2642c23275873f9404532df163394c62b 100644 (file)
@@ -490,6 +490,12 @@ static int __pyx_Generator_init(void); /*proto*/
 
 #include <structmember.h>
 #include <frameobject.h>
+#if PY_VERSION_HEX >= 0x030b00a6
+  #ifndef Py_BUILD_CORE
+    #define Py_BUILD_CORE 1
+  #endif
+  #include "internal/pycore_frame.h"
+#endif
 
 #define __Pyx_Coroutine_Undelegate(gen) Py_CLEAR((gen)->yieldfrom)
 
index 5ea48ea947b07cb1e76a4c4930b5693c3084d35e..b2c67dcce0d326ee27b59e14ee3a62f6f0061b8d 100644 (file)
@@ -660,6 +660,9 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P
         self = PyTuple_GetItem(args, 0);
         if (unlikely(!self)) {
             Py_DECREF(new_args);
+            PyErr_Format(PyExc_TypeError,
+                         "unbound method %.200S() needs an argument",
+                         cyfunc->func_qualname);
             return NULL;
         }
 
index b0411f6956b6c090d244cc5b1055e388d8ea5ceb..2cd4b604e0b49f6b2f885c2508504ed263a5459d 100644 (file)
@@ -705,6 +705,12 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line,
 #include "compile.h"
 #include "frameobject.h"
 #include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+  #ifndef Py_BUILD_CORE
+    #define Py_BUILD_CORE 1
+  #endif
+  #include "internal/pycore_frame.h"
+#endif
 
 static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
             const char *funcname, int c_line,
@@ -772,6 +778,7 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line,
     PyCodeObject *py_code = 0;
     PyFrameObject *py_frame = 0;
     PyThreadState *tstate = __Pyx_PyThreadState_Current;
+    PyObject *ptype, *pvalue, *ptraceback;
 
     if (c_line) {
         c_line = __Pyx_CLineForTraceback(tstate, c_line);
@@ -780,9 +787,18 @@ static void __Pyx_AddTraceback(const char *funcname, int c_line,
     // Negate to avoid collisions between py and c lines.
     py_code = $global_code_object_cache_find(c_line ? -c_line : py_line);
     if (!py_code) {
+        __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback);
         py_code = __Pyx_CreateCodeObjectForTraceback(
             funcname, c_line, py_line, filename);
-        if (!py_code) goto bad;
+        if (!py_code) {
+            /* If the code object creation fails, then we should clear the
+               fetched exception references and propagate the new exception */
+            Py_XDECREF(ptype);
+            Py_XDECREF(pvalue);
+            Py_XDECREF(ptraceback);
+            goto bad;
+        }
+        __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback);
         $global_code_object_cache_insert(c_line ? -c_line : py_line, py_code);
     }
     py_frame = PyFrame_New(
index 0d8c41dee139d7a0e1e2a5032b3522db85c54054..dc187ab49e8c83d759d4a98b344b2273031ac7ad 100644 (file)
@@ -204,18 +204,37 @@ static int __Pyx_setup_reduce_is_named(PyObject* meth, PyObject* name) {
 static int __Pyx_setup_reduce(PyObject* type_obj) {
     int ret = 0;
     PyObject *object_reduce = NULL;
+    PyObject *object_getstate = NULL;
     PyObject *object_reduce_ex = NULL;
     PyObject *reduce = NULL;
     PyObject *reduce_ex = NULL;
     PyObject *reduce_cython = NULL;
     PyObject *setstate = NULL;
     PyObject *setstate_cython = NULL;
+    PyObject *getstate = NULL;
 
 #if CYTHON_USE_PYTYPE_LOOKUP
-    if (_PyType_Lookup((PyTypeObject*)type_obj, PYIDENT("__getstate__"))) goto __PYX_GOOD;
+    getstate = _PyType_Lookup((PyTypeObject*)type_obj, PYIDENT("__getstate__"));
 #else
-    if (PyObject_HasAttr(type_obj, PYIDENT("__getstate__"))) goto __PYX_GOOD;
+    getstate = __Pyx_PyObject_GetAttrStrNoError(type_obj, PYIDENT("__getstate__"));
+    if (!getstate && PyErr_Occurred()) {
+        goto __PYX_BAD;
+    }
 #endif
+    if (getstate) {
+        // Python 3.11 introduces object.__getstate__. Because it's version-specific failure to find it should not be an error
+#if CYTHON_USE_PYTYPE_LOOKUP
+        object_getstate = _PyType_Lookup(&PyBaseObject_Type, PYIDENT("__getstate__"));
+#else
+        object_getstate = __Pyx_PyObject_GetAttrStrNoError((PyObject*)&PyBaseObject_Type, PYIDENT("__getstate__"));
+        if (!object_getstate && PyErr_Occurred()) {
+            goto __PYX_BAD;
+        }
+#endif
+        if (object_getstate != getstate) {
+            goto __PYX_GOOD;
+        }
+    }
 
 #if CYTHON_USE_PYTYPE_LOOKUP
     object_reduce_ex = _PyType_Lookup(&PyBaseObject_Type, PYIDENT("__reduce_ex__")); if (!object_reduce_ex) goto __PYX_BAD;
@@ -270,6 +289,8 @@ __PYX_GOOD:
 #if !CYTHON_USE_PYTYPE_LOOKUP
     Py_XDECREF(object_reduce);
     Py_XDECREF(object_reduce_ex);
+    Py_XDECREF(object_getstate);
+    Py_XDECREF(getstate);
 #endif
     Py_XDECREF(reduce);
     Py_XDECREF(reduce_ex);
index f9009be0980c0464c0ddc1fa8b7e3475d1b5440e..87f60e1a28d20cd35baba5a50e496ee7731b9944 100644 (file)
@@ -83,6 +83,9 @@
   #define CYTHON_USE_DICT_VERSIONS 0
   #undef CYTHON_USE_EXC_INFO_STACK
   #define CYTHON_USE_EXC_INFO_STACK 0
+  #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+    #define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_HEX >= 0x07030900)
+  #endif
 
 #elif defined(PYSTON_VERSION)
   #define CYTHON_COMPILING_IN_PYPY 0
   #define CYTHON_USE_DICT_VERSIONS 0
   #undef CYTHON_USE_EXC_INFO_STACK
   #define CYTHON_USE_EXC_INFO_STACK 0
+  #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+    #define CYTHON_UPDATE_DESCRIPTOR_DOC 0
+  #endif
 
 #else
   #define CYTHON_COMPILING_IN_PYPY 0
   #ifndef CYTHON_FAST_PYCALL
     // Python 3.11 deleted localplus argument from frame object, which is used in our
     // fast_pycall code
-    #define CYTHON_FAST_PYCALL (PY_VERSION_HEX < 0x030B00A1)
+    // On Python 3.10 it causes issues when used while profiling/debugging
+    #define CYTHON_FAST_PYCALL (PY_VERSION_HEX < 0x030A0000)
   #endif
   #ifndef CYTHON_PEP489_MULTI_PHASE_INIT
     #define CYTHON_PEP489_MULTI_PHASE_INIT (PY_VERSION_HEX >= 0x03050000)
   #elif !defined(CYTHON_USE_EXC_INFO_STACK)
     #define CYTHON_USE_EXC_INFO_STACK (PY_VERSION_HEX >= 0x030700A3)
   #endif
+  #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC
+    #define CYTHON_UPDATE_DESCRIPTOR_DOC 1
+  #endif
 #endif
 
 #if !defined(CYTHON_FAST_PYCCALL)
@@ -963,8 +973,10 @@ static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObj
 
 /////////////// MathInitCode ///////////////
 
-#if defined(WIN32) || defined(MS_WINDOWS)
-  #define _USE_MATH_DEFINES
+#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS)
+  #if !defined(_USE_MATH_DEFINES)
+    #define _USE_MATH_DEFINES
+  #endif
 #endif
 #include <math.h>
 
@@ -1211,11 +1223,37 @@ static int __Pyx_check_binary_version(void);
 /////////////// CheckBinaryVersion ///////////////
 
 static int __Pyx_check_binary_version(void) {
-    char ctversion[4], rtversion[4];
-    PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
-    PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion());
-    if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) {
+    char ctversion[5];
+    int same=1, i, found_dot;
+    const char* rt_from_call = Py_GetVersion();
+    PyOS_snprintf(ctversion, 5, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
+    // slightly convoluted, but now that we're into double digit version numbers we can no longer just rely on the length.
+    found_dot = 0;
+    for (i = 0; i < 4; i++) {
+        if (!ctversion[i]) {
+            // if they are the same, just check that the runtime version doesn't continue with further numbers
+            same = (rt_from_call[i] < '0' || rt_from_call[i] > '9');
+            break;
+        }
+        if (rt_from_call[i] != ctversion[i]) {
+            same = 0;
+            break;
+        }
+    }
+
+    if (!same) {
+        char rtversion[5] = {'\0'};
+        // copy the runtime-version for the error message
         char message[200];
+        for (i=0; i<4; ++i) {
+            if (rt_from_call[i] == '.') {
+                if (found_dot) break;
+                found_dot = 1;
+            } else if (rt_from_call[i] < '0' || rt_from_call[i] > '9') {
+                break;
+            }
+            rtversion[i] = rt_from_call[i];
+        }
         PyOS_snprintf(message, sizeof(message),
                       "compiletime version %s of module '%.100s' "
                       "does not match runtime version %s",
@@ -1272,6 +1310,11 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void)
   #define __Pyx_RefNannySetupContext(name, acquire_gil) \
           __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
 #endif
+  #define __Pyx_RefNannyFinishContextNogil() { \
+              PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure(); \
+              __Pyx_RefNannyFinishContext(); \
+              PyGILState_Release(__pyx_gilstate_save); \
+          }
   #define __Pyx_RefNannyFinishContext() \
           __Pyx_RefNanny->FinishContext(&__pyx_refnanny)
   #define __Pyx_INCREF(r)  __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
@@ -1285,6 +1328,7 @@ static CYTHON_INLINE int __Pyx_Is_Little_Endian(void)
 #else
   #define __Pyx_RefNannyDeclarations
   #define __Pyx_RefNannySetupContext(name, acquire_gil)
+  #define __Pyx_RefNannyFinishContextNogil()
   #define __Pyx_RefNannyFinishContext()
   #define __Pyx_INCREF(r) Py_INCREF(r)
   #define __Pyx_DECREF(r) Py_DECREF(r)
index c1b1c60bdad581bd478f9008b47af73dc1c04fbb..864b658f7453abcb8077409a88d48c616cf7928d 100644 (file)
@@ -1996,6 +1996,13 @@ static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args,
   static size_t __pyx_pyframe_localsplus_offset = 0;
 
   #include "frameobject.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+  #ifndef Py_BUILD_CORE
+    #define Py_BUILD_CORE 1
+  #endif
+  #include "internal/pycore_frame.h"
+#endif
+
   // This is the long runtime version of
   //     #define __Pyx_PyFrame_GetLocalsplus(frame)  ((frame)->f_localsplus)
   // offsetof(PyFrameObject, f_localsplus) differs between regular C-Python and Stackless Python.
index 428cb1db4a0e348001832810e18e7979a26426e8..a0ab1fa989b5447dc76d42bf6f287a6fe892620f 100644 (file)
   #include "compile.h"
   #include "frameobject.h"
   #include "traceback.h"
+#if PY_VERSION_HEX >= 0x030b00a6
+  #ifndef Py_BUILD_CORE
+    #define Py_BUILD_CORE 1
+  #endif
+  #include "internal/pycore_frame.h"
+#endif
 
   #if CYTHON_PROFILE_REUSE_FRAME
     #define CYTHON_FRAME_MODIFIER static
index a7559ec03c5f05c8a0f65ee79d4f4ac454f03318..35241c64a463a835f016f3369c22a10877b44dbf 100644 (file)
@@ -314,7 +314,7 @@ static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int eq
             return (equals == Py_EQ);
         } else {
             int result;
-#if CYTHON_USE_UNICODE_INTERNALS
+#if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000)
             Py_hash_t hash1, hash2;
             hash1 = ((PyBytesObject*)s1)->ob_shash;
             hash2 = ((PyBytesObject*)s2)->ob_shash;
index 1020a3fa7058e29e5c90001210633e2972c3394c..8d78bc0e4930b91a7767e3cff728dd704b3f08d6 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,11 @@
 Metadata-Version: 2.1
 Name: Cython
-Version: 0.29.28
+Version: 0.29.29
 Summary: The Cython compiler for writing C extensions for the Python language.
 Home-page: http://cython.org/
 Author: Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.
 Author-email: cython-devel@python.org
 License: Apache
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
@@ -56,5 +55,3 @@ to install an uncompiled (slower) version of Cython with::
     pip install Cython --install-option="--no-cython-compile"
 
 .. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/
-
-
index 63b40787185d565446fe35ce77bd02033e51a868..6ad953ac92ae86929197464707b765f76571d4b4 100644 (file)
@@ -675,7 +675,7 @@ cannot be pickled) decorate the class with ``@cython.auto_pickle(True)``.
 One can also annotate with ``@cython.auto_pickle(False)`` to get the old
 behavior of not generating a ``__reduce__`` method in any case.
 
-Manually implementing a ``__reduce__`` or `__reduce_ex__`` method will also
+Manually implementing a ``__reduce__`` or ``__reduce_ex__`` method will also
 disable this auto-generation and can be used to support pickling of more
 complicated types.
 
index e2508f9d49ef3471646b5a1564848976d65c2797..c1de7b8685df7ca7ac86e207c32dfcfa67b8957e 100644 (file)
@@ -7,13 +7,13 @@ PYTHON package_test.py
 
 import os.path
 
-with open("pkg/test.c.dep", "r") as f:
+with open(os.path.join("pkg", "test.c.dep"), "r") as f:
     contents = f.read().replace("\\\n", " ").replace("\n", " ")
 
 assert sorted(contents.split()) == sorted(['test.c:', os.path.join('sub', 'incl.pxi'), 'test.pxd', 'test.pyx']), contents
 
 
-with open("pkg/sub/test.c.dep", "r") as f:
+with open(os.path.join("pkg", "sub", "test.c.dep"), "r") as f:
     contents = f.read().replace("\\\n", " ").replace("\n", " ")
 
 contents = [os.path.relpath(entry, '.')
index bcfda81f0d66147f93b121ff331bdc943464bc58..e02326598aa3367a46625c250d36ddc43e39bbdd 100644 (file)
@@ -4,7 +4,21 @@
 
 cimport cython
 
+# The "contains" tests relate to GH-4785 - replacing the method
+# call with PySequence_Contains was causing infinite recursion
+# for some classes
+
 cdef class MyList(list):
+    """
+    >>> l = MyList()
+    >>> l.__contains__(1)
+    MyList.__contains__
+    False
+    >>> l.append(1)
+    >>> l.__contains__(1)
+    MyList.__contains__
+    True
+    """
     def test_append(self, x):
         """
         >>> l = MyList()
@@ -18,7 +32,13 @@ cdef class MyList(list):
         """
         self.append(x)
 
+    def __contains__(self, value):
+        print "MyList.__contains__"
+        return list.__contains__(self, value)  # probably optimized
+
 cdef class MyDict(dict):
+    # tests for __contains__ are in the global __doc__ to version-check a PyPy bug
+
     @cython.test_assert_path_exists("//ComprehensionNode//AttributeNode",
                                     "//ComprehensionNode//AttributeNode[@attribute='items']")
     @cython.test_fail_if_path_exists("//ComprehensionNode//CMethodSelfCloneNode")
@@ -40,6 +60,19 @@ cdef class MyDict(dict):
         l.sort()
         return l
 
+    def __contains__(self, key):
+        print "MyDict.__contains__"
+        return dict.__contains__(self, key)
+
+import sys
+pypy_version = getattr(sys, 'pypy_version_info', None)
+if not (pypy_version and pypy_version < (7, 3, 10)):
+    __doc__ = """
+    >>> MyDict(a=1).__contains__("a")
+    MyDict.__contains__
+    True
+    """
+
 @cython.final
 cdef class MyDictFinal(dict):
     @cython.test_assert_path_exists("//ComprehensionNode//CMethodSelfCloneNode")
@@ -155,3 +188,17 @@ cdef class MyDictOverride2(MyDict):
         l = [ v for v in self.values() ]
         l.sort()
         return l
+
+class MyBytes(bytes):
+    """
+    >>> mb = MyBytes(b"abc")
+    >>> mb.__contains__(b"a")
+    MyBytes.__contains__
+    True
+    >>> mb.__contains__(b"z")
+    MyBytes.__contains__
+    False
+    """
+    def __contains__(self, value):
+        print "MyBytes.__contains__"
+        return bytes.__contains__(self, value)
diff --git a/tests/run/call_trace_gh4609.srctree b/tests/run/call_trace_gh4609.srctree
new file mode 100644 (file)
index 0000000..850be44
--- /dev/null
@@ -0,0 +1,44 @@
+PYTHON setup.py build_ext -i
+PYTHON run.py
+
+######## setup.py ########
+
+from Cython.Build.Dependencies import cythonize
+from distutils.core import setup
+
+setup(
+  ext_modules = cythonize("*.pyx"),
+)
+
+####### call_func.pyx ##########
+
+import mod
+
+def cy_method(x):
+    return mod.function(x)
+    
+####### mod.py #################
+
+mod_global = "this is a mod global"
+
+def function(a, mod_global=None):
+    if mod_global is not None:
+        mod_global = a
+    
+    return
+    
+####### run.py #################
+
+from call_func import cy_method
+import mod
+import sys
+
+def trace(frame, event, arg):
+    assert(mod.mod_global == "this is a mod global")
+
+    return trace
+    
+sys.settrace(trace)
+
+cy_method("s")
+assert(mod.mod_global == "this is a mod global")
index c8d9d82056ff1d2e966dbce4bd046b7b76d29f79..69c90d90a75afaa38a46e6e6b156bf038daca3f2 100644 (file)
@@ -1,4 +1,6 @@
 """
+>>> import sys
+
 >>> ONE, TEN, HUNDRED
 (1, 10, 100)
 >>> THOUSAND        # doctest: +ELLIPSIS
@@ -35,8 +37,10 @@ NameError: ...name 'RANK_3' is not defined
 
 >>> set(PyxEnum) == {TWO, THREE, FIVE}
 True
->>> str(PyxEnum.TWO).split(".")[-1]  # Py3.10 changed the output here
+>>> str(PyxEnum.TWO).split(".")[-1]  if sys.version_info < (3,11) else  "TWO" # Py3.10/11 changed the output here
 'TWO'
+>>> str(PyxEnum.TWO)  if sys.version_info >= (3,11) else  "2" # Py3.10/11 changed the output here
+'2'
 >>> PyxEnum.TWO + PyxEnum.THREE == PyxEnum.FIVE
 True
 >>> PyxEnum(2) is PyxEnum["TWO"] is PyxEnum.TWO
index 3811b2694d59cf30674c5e45535c1efe77da9f64..f61dddb53564956f029ce42cbf0935ce8a1ee9b9 100644 (file)
@@ -363,6 +363,9 @@ cdef class TestUnboundMethodCdef:
     >>> C = TestUnboundMethodCdef
     >>> IS_PY2 or (C.meth is C.__dict__["meth"])
     True
+    >>> TestUnboundMethodCdef.meth()  # doctest:+ELLIPSIS
+    Traceback (most recent call last):
+    TypeError: ...
     """
     def meth(self): pass
 
@@ -372,6 +375,9 @@ class TestUnboundMethod:
     >>> C = TestUnboundMethod
     >>> IS_PY2 or (C.meth is C.__dict__["meth"])
     True
+    >>> TestUnboundMethod.meth()  # doctest:+ELLIPSIS
+    Traceback (most recent call last):
+    TypeError: ...
     """
     def meth(self): pass
 
index 59eeb739adf3e09370709dc4e0611083408f67a5..00d005d6cf45cfa2c9c9706e6a6ba45ecabdfa03 100644 (file)
@@ -1,3 +1,5 @@
+# mode: run
+
 class Foo:
     @property
     def foo(self):
@@ -36,6 +38,6 @@ def wrap_hasattr(obj, name):
     >>> hasattr(Foo(), None)   #doctest: +ELLIPSIS
     Traceback (most recent call last):
        ...
-    TypeError: hasattr(): attribute name must be string
+    TypeError: ...attribute name must be string...
     """
     return hasattr(obj, name)
index c7b4c7d57ef78cbdcfec8794f2e1a3e3074c19d6..44dea3abc8874d1b555fdc333a5dec31a4136e83 100644 (file)
@@ -9,7 +9,7 @@ def modobj(obj2, obj3):
     '5'
     >>> modobj(1, 0)  # doctest: +ELLIPSIS
     Traceback (most recent call last):
-    ZeroDivisionError: integer division...
+    ZeroDivisionError: integer ...
     """
     obj1 = obj2 % obj3
     return obj1
@@ -19,7 +19,7 @@ def mod_10_obj(int2):
     """
     >>> mod_10_obj(0)  # doctest: +ELLIPSIS
     Traceback (most recent call last):
-    ZeroDivisionError: integer division...
+    ZeroDivisionError: integer ...
     >>> mod_10_obj(3)
     1
     """
index 537f720a10275a0f81d459410ab26cba1c7aded0..4e8e8b2736e9cacbd56e24df90d40d579b014eaf 100644 (file)
@@ -201,6 +201,16 @@ cdef class NoPyMembers(object):
     def __repr__(self):
         return "%s(ii=%s, x=%s)" % (type(self).__name__, self.ii, self.x)
 
+    def __eq__(self, other):
+        return (
+            isinstance(other, NoPyMembers) and
+            (<NoPyMembers> other).ii[0] == self.ii[0] and
+            (<NoPyMembers> other).ii[1] == self.ii[1] and
+            (<NoPyMembers> other).ii[2] == self.ii[2] and
+            (<NoPyMembers> other).x == self.x
+        )
+
+
 class NoPyMembersPySubclass(NoPyMembers):
     """
     >>> import pickle
@@ -304,3 +314,24 @@ if sys.version_info[:2] >= (3, 5):
         """
         def my_method(self, x):
             return x
+
+
+# Pickled with Cython 0.29.28 (using MD5 for the checksum).
+OLD_MD5_PICKLE = b'''\
+creduce_pickle\n__pyx_unpickle_NoPyMembers\nq\x00\
+(creduce_pickle\nNoPyMembers\nq\x01J\xf2K_\n(]q\x02\
+(K\x0bKyM3\x05eG?\xf8\x00\x00\x00\x00\x00\x00tq\x03tq\x04Rq\x05.\
+'''
+
+try:
+    from hashlib import md5
+except ImportError:
+    pass
+else:
+    def unpickle_old_0_29_28():
+        """
+        >>> import pickle
+        >>> b = pickle.loads(OLD_MD5_PICKLE)
+        >>> b == NoPyMembers(i=11, x=1.5) or b
+        True
+        """
index 350d452f45e50a4ea07631f8855667762acabcc0..e5d1ec5bced9270db943f828097875c41626c9fd 100644 (file)
@@ -923,3 +923,27 @@ cdef class VerySpecialSubType(VerySpecial):
 
     def __get__(self, inst, own):
         return VerySpecial.__get__(self, inst, own)
+
+
+cdef class ReverseMethodsExist:
+    """
+    reverse methods (such as __radd__) don't work in Cython <3. However, if they
+    are defined then it should be possible to look them up explicitly instead of
+    looking up autogenerated wrapper (which points to the forward method)
+
+    >>> o = ReverseMethodsExist()
+    >>> o + o
+    'add'
+    >>> o.__add__(o)
+    'add'
+    >>> o.__radd__(o)
+    'radd'
+    >>> o.__rsub__(o)
+    'rsub'
+    """
+    def __add__(self, other):
+        return "add"
+    def __radd__(self, other):
+        return "radd"
+    def __rsub__(self, other):
+        return "rsub"
index 4616acf2c7dec29d2d793f62335ed77b756029fc..179081daf8927feaf2338c5669f9749808b47f55 100644 (file)
@@ -1482,7 +1482,7 @@ class UnicodeTest(CommonTest,
         self.assertEqual(('...%(foo)s...' % {'foo':Str.ABC}).replace("Str.", ""),
                          '...ABC...')
         self.assertEqual(('...%(foo)s...' % {'foo':Int.IDES}).replace("Int.", ""),
-                         '...IDES...')
+                         '...IDES...' if sys.version_info < (3,11) else '...15...')
         self.assertEqual('...%(foo)i...' % {'foo':Int.IDES},
                          '...15...')
         self.assertEqual('...%(foo)d...' % {'foo':Int.IDES},
diff --git a/tests/run/trace_nogil.pyx b/tests/run/trace_nogil.pyx
new file mode 100644 (file)
index 0000000..dee443e
--- /dev/null
@@ -0,0 +1,22 @@
+# cython: linetrace=True
+
+cdef void foo(int err) nogil except *:
+    with gil:
+        raise ValueError(err)
+
+
+# Test from gh-4637
+def handler(int err):
+    """
+    >>> handler(0)
+    All good
+    >>> handler(1)    # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+        ...
+    ValueError: 1
+    """
+    if (err % 2):
+        with nogil:
+            foo(err)
+    else:
+        print("All good")
index dddf7de9b233826d6a6294e14ff128ad31a5d2e6..1acc1f3ab60e246d7819c1c5a9e0fdc5c6c4764c 100644 (file)
@@ -501,3 +501,20 @@ def complete_except_as_raise(x, a, b):
     else:
         i = 5
     return i
+
+
+def try_except_raise_new(initial, catch, throw):
+    """
+    >>> try_except_raise_new(None, TypeError, ValueError)
+    >>> try_except_raise_new(TypeError, IndexError, ValueError)
+    Traceback (most recent call last):
+    TypeError
+    >>> try_except_raise_new(TypeError, TypeError, ValueError)
+    Traceback (most recent call last):
+    ValueError
+    """
+    try:
+        if initial is not None:
+            raise initial
+    except catch:
+        raise throw
index 7ff545308d0d0f0c9cd1b4ef61c3a192dd134140..072019a99022bded2dc86345b31e13ac9b38fddb 100644 (file)
@@ -549,3 +549,21 @@ def complex_finally_clause(x, obj):
             del l[0], lobj[0]
             assert all(i == 3 for i in l), l
     return 99
+
+def function_in_finally():
+    """
+    https://github.com/cython/cython/issues/4651 - function definitions in the
+    except copy of the finally clause weren't generated
+
+    >>> function_in_finally()
+    in try
+    in func()
+    finished
+    """
+    try:
+        print('in try')
+    finally:
+        def func():
+            print('in func()')
+        func()
+    print('finished')