Imported Upstream version 0.29.20 upstream/0.29.20
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 03:11:17 +0000 (12:11 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 03:11:17 +0000 (12:11 +0900)
26 files changed:
CHANGES.rst
Cython/Compiler/Code.py
Cython/Compiler/ExprNodes.py
Cython/Compiler/FlowControl.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/Options.py
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.py
Cython/Compiler/TypeSlots.py
Cython/Shadow.py
Cython/Utility/Builtins.c
Cython/Utility/CythonFunction.c
Cython/Utility/ExtensionTypes.c
Cython/Utility/ModuleSetupCode.c
Cython/Utility/Optimize.c
runtests.py
tests/errors/e_cpp_only_features.pyx [new file with mode: 0644]
tests/errors/e_fstring.pyx [new file with mode: 0644]
tests/errors/w_uninitialized.pyx
tests/errors/w_uninitialized_cpp.pyx [new file with mode: 0644]
tests/run/builtin_abs.pyx
tests/run/classmethod.pyx
tests/run/cython3.pyx
tests/run/fused_types.pyx
tests/run/verbatiminclude.pyx

index 64fd0bac952eef408ba439afbe99983e24bdb9c6..166dfee972933ec49f80c830a42e5bd8dc96b79f 100644 (file)
@@ -2,6 +2,44 @@
 Cython Changelog
 ================
 
+0.29.20 (2020-06-10)
+====================
+
+Bugs fixed
+----------
+
+* Nested try-except statements with multiple ``return`` statements could crash
+  due to incorrect deletion of the ``except as`` target variable.
+  (Github issue #3666)
+
+* The ``@classmethod`` decorator no longer rejects unknown input from other decorators.
+  Patch by David Woods.  (Github issue #3660)
+
+* Fused types could leak into unrelated usages.
+  Patch by David Woods.  (Github issue #3642)
+
+* Now uses ``Py_SET_SIZE()`` and ``Py_SET_REFCNT()`` in Py3.9+ to avoid low-level
+  write access to these object fields.
+  Patch by Victor Stinner.  (Github issue #3639)
+
+* The built-in ``abs()`` function could lead to undefined behaviour when used on
+  the negative-most value of a signed C integer type.
+  Patch by Serge Guelton.  (Github issue #1911)
+
+* Usages of ``sizeof()`` and ``typeid()`` on uninitialised variables no longer
+  produce a warning.
+  Patch by Celelibi.  (Github issue #3575)
+
+* The C++ ``typeid()`` function was allowed in C mode.
+  Patch by Celelibi.  (Github issue #3637)
+
+* The error position reported for errors found in f-strings was misleading.
+  (Github issue #3674)
+
+* The new ``c_api_binop_methods`` directive was added for forward compatibility, but can
+  only be set to True (the current default value).  It can be disabled in Cython 3.0.
+
+
 0.29.19 (2020-05-20)
 ====================
 
index b9c689ce7641ce1680058df0fd3de6c7f8326144..515a5355d4e55e8fcebaa19356f1a7a12333246b 100644 (file)
@@ -543,7 +543,7 @@ class UtilityCode(UtilityCodeBase):
 
         impl = re.sub(r'PY(IDENT|UNICODE)\("([^"]+)"\)', externalise, impl)
         assert 'PYIDENT(' not in impl and 'PYUNICODE(' not in impl
-        return bool(replacements), impl
+        return True, impl
 
     def inject_unbound_methods(self, impl, output):
         """Replace 'UNBOUND_METHOD(type, "name")' by a constant Python identifier cname.
@@ -551,7 +551,6 @@ class UtilityCode(UtilityCodeBase):
         if 'CALL_UNBOUND_METHOD(' not in impl:
             return False, impl
 
-        utility_code = set()
         def externalise(matchobj):
             type_cname, method_name, obj_cname, args = matchobj.groups()
             args = [arg.strip() for arg in args[1:].split(',')] if args else []
@@ -567,9 +566,7 @@ class UtilityCode(UtilityCodeBase):
             r'\)', externalise, impl)
         assert 'CALL_UNBOUND_METHOD(' not in impl
 
-        for helper in sorted(utility_code):
-            output.use_utility_code(UtilityCode.load_cached(helper, "ObjectHandling.c"))
-        return bool(utility_code), impl
+        return True, impl
 
     def wrap_c_strings(self, impl):
         """Replace CSTRING('''xyz''') by a C compatible string
index 7d184e66a3c35f8461e9fcb5fed5b83eb29124ab..a0baadf8ee1e63ea51c308db21bd4cdc296c106d 100644 (file)
@@ -5471,7 +5471,7 @@ class CallNode(ExprNode):
         func_type = self.function_type()
         if func_type.is_pyobject:
             self.gil_error()
-        elif not getattr(func_type, 'nogil', False):
+        elif not func_type.is_error and not getattr(func_type, 'nogil', False):
             self.gil_error()
 
     gil_message = "Calling gil-requiring function"
@@ -5839,6 +5839,17 @@ class SimpleCallNode(CallNode):
         if function.is_name or function.is_attribute:
             code.globalstate.use_entry_utility_code(function.entry)
 
+        abs_function_cnames = ('abs', 'labs', '__Pyx_abs_longlong')
+        is_signed_int = self.type.is_int and self.type.signed
+        if self.overflowcheck and is_signed_int and function.result() in abs_function_cnames:
+            code.globalstate.use_utility_code(UtilityCode.load_cached("Common", "Overflow.c"))
+            code.putln('if (unlikely(%s == __PYX_MIN(%s))) {\
+                PyErr_SetString(PyExc_OverflowError,\
+                                "Trying to take the absolute value of the most negative integer is not defined."); %s; }' % (
+                            self.args[0].result(),
+                            self.args[0].type.empty_declaration_code(),
+                            code.error_goto(self.pos)))
+
         if not function.type.is_pyobject or len(self.arg_tuple.args) > 1 or (
                 self.arg_tuple.args and self.arg_tuple.is_literal):
             super(SimpleCallNode, self).generate_evaluation_code(code)
@@ -5941,13 +5952,7 @@ class SimpleCallNode(CallNode):
                                             self.result() if self.type.is_pyobject else None,
                                             func_type.exception_value, self.nogil)
                 else:
-                    if (self.overflowcheck
-                        and self.type.is_int
-                        and self.type.signed
-                        and self.function.result() in ('abs', 'labs', '__Pyx_abs_longlong')):
-                        goto_error = 'if (unlikely(%s < 0)) { PyErr_SetString(PyExc_OverflowError, "value too large"); %s; }' % (
-                            self.result(), code.error_goto(self.pos))
-                    elif exc_checks:
+                    if exc_checks:
                         goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
                     else:
                         goto_error = ""
@@ -10866,7 +10871,10 @@ class TypeidNode(ExprNode):
         typeinfo_entry = typeinfo_module.lookup('type_info')
         return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type))
 
+    cpp_message = 'typeid operator'
+
     def analyse_types(self, env):
+        self.cpp_check(env)
         type_info = self.get_type_info_type(env)
         if not type_info:
             self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
index b4a9356250d125c050c831753132701354e5e458..49f74a2e60a8b5f1f0fea981fd7b4570e41096c0 100644 (file)
@@ -884,6 +884,12 @@ class ControlFlowAnalysis(CythonTransform):
         self.mark_position(node)
         return node
 
+    def visit_SizeofVarNode(self, node):
+        return node
+
+    def visit_TypeidNode(self, node):
+        return node
+
     def visit_IfStatNode(self, node):
         next_block = self.flow.newblock()
         parent = self.flow.block
@@ -1192,8 +1198,6 @@ class ControlFlowAnalysis(CythonTransform):
         if self.flow.loops:
             self.flow.loops[-1].exceptions.append(descr)
         self.flow.block = body_block
-        ## XXX: Is it still required
-        body_block.add_child(entry_point)
         self.flow.nextblock()
         self._visit(node.body)
         self.flow.exceptions.pop()
@@ -1227,11 +1231,18 @@ class ControlFlowAnalysis(CythonTransform):
         self.mark_position(node)
         self.visitchildren(node)
 
-        for exception in self.flow.exceptions[::-1]:
-            if exception.finally_enter:
-                self.flow.block.add_child(exception.finally_enter)
-                if exception.finally_exit:
-                    exception.finally_exit.add_child(self.flow.exit_point)
+        outer_exception_handlers = iter(self.flow.exceptions[::-1])
+        for handler in outer_exception_handlers:
+            if handler.finally_enter:
+                self.flow.block.add_child(handler.finally_enter)
+                if handler.finally_exit:
+                    # 'return' goes to function exit, or to the next outer 'finally' clause
+                    exit_point = self.flow.exit_point
+                    for next_handler in outer_exception_handlers:
+                        if next_handler.finally_enter:
+                            exit_point = next_handler.finally_enter
+                            break
+                    handler.finally_exit.add_child(exit_point)
                 break
         else:
             if self.flow.block:
index ede21ff09b8e806b13fd09ee84e1af575bc2e38c..e1e925086f8b8d12259ef086eae4aa04ab15116b 100644 (file)
@@ -29,7 +29,7 @@ from . import Pythran
 from .Errors import error, warning
 from .PyrexTypes import py_object_type
 from ..Utils import open_new_file, replace_suffix, decode_filename, build_hex_version
-from .Code import UtilityCode, IncludeCode
+from .Code import UtilityCode, IncludeCode, TempitaUtilityCode
 from .StringEncoding import EncodedString
 from .Pythran import has_np_pythran
 
@@ -1255,6 +1255,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                         self.generate_dict_getter_function(scope, code)
                     if scope.defines_any_special(TypeSlots.richcmp_special_methods):
                         self.generate_richcmp_function(scope, code)
+                    for slot in TypeSlots.PyNumberMethods:
+                        if slot.is_binop and scope.defines_any_special(slot.user_methods):
+                            self.generate_binop_function(scope, slot, code)
                     self.generate_property_accessors(scope, code)
                     self.generate_method_table(scope, code)
                     self.generate_getset_table(scope, code)
@@ -1554,9 +1557,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         code.putln("{")
         code.putln("PyObject *etype, *eval, *etb;")
         code.putln("PyErr_Fetch(&etype, &eval, &etb);")
-        code.putln("++Py_REFCNT(o);")
+        # increase the refcount while we are calling into user code
+        # to prevent recursive deallocation
+        code.putln("__Pyx_SET_REFCNT(o, Py_REFCNT(o) + 1);")
         code.putln("%s(o);" % entry.func_cname)
-        code.putln("--Py_REFCNT(o);")
+        code.putln("__Pyx_SET_REFCNT(o, Py_REFCNT(o) - 1);")
         code.putln("PyErr_Restore(etype, eval, etb);")
         code.putln("}")
 
@@ -1892,6 +1897,64 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         code.putln("}")  # switch
         code.putln("}")
 
+    def generate_binop_function(self, scope, slot, code):
+        func_name = scope.mangle_internal(slot.slot_name)
+        if scope.directives['c_api_binop_methods']:
+            code.putln('#define %s %s' % (func_name, slot.left_slot.slot_code(scope)))
+            return
+        else:
+            error(self.pos,
+                  "The 'c_api_binop_methods' directive is only supported for forward compatibility"
+                  " and must be True.")
+
+        code.putln()
+        preprocessor_guard = slot.preprocessor_guard_code()
+        if preprocessor_guard:
+            code.putln(preprocessor_guard)
+
+        if slot.left_slot.signature == TypeSlots.binaryfunc:
+            slot_type = 'binaryfunc'
+            extra_arg = extra_arg_decl = ''
+        elif slot.left_slot.signature == TypeSlots.ternaryfunc:
+            slot_type = 'ternaryfunc'
+            extra_arg = ', extra_arg'
+            extra_arg_decl = ', PyObject* extra_arg'
+        else:
+            error(entry.pos, "Unexpected type lost signature: %s" % slot)
+
+        def has_slot_method(method_name):
+            entry = scope.lookup(method_name)
+            return bool(entry and entry.is_special and entry.func_cname)
+        def call_slot_method(method_name, reverse):
+            entry = scope.lookup(method_name)
+            if entry and entry.is_special and entry.func_cname:
+                return "%s(%s%s)" % (
+                    entry.func_cname,
+                    "right, left" if reverse else "left, right",
+                    extra_arg)
+            else:
+                return '%s_maybe_call_slot(%s, left, right %s)' % (
+                    func_name,
+                    'Py_TYPE(right)->tp_base' if reverse else 'Py_TYPE(left)->tp_base',
+                    extra_arg)
+
+        code.putln(
+            TempitaUtilityCode.load_as_string(
+                "BinopSlot", "ExtensionTypes.c",
+                context={
+                    "func_name": func_name,
+                    "slot_name": slot.slot_name,
+                    "overloads_left": int(has_slot_method(slot.left_slot.method_name)),
+                    "call_left": call_slot_method(slot.left_slot.method_name, reverse=False),
+                    "call_right": call_slot_method(slot.right_slot.method_name, reverse=True),
+                    "type_cname": scope.parent_type.typeptr_cname,
+                    "slot_type": slot_type,
+                    "extra_arg": extra_arg,
+                    "extra_arg_decl": extra_arg_decl,
+                    })[1])
+        if preprocessor_guard:
+            code.putln("#endif")
+
     def generate_getattro_function(self, scope, code):
         # First try to get the attribute using __getattribute__, if defined, or
         # PyObject_GenericGetAttr.
index 8bda3ffcb525328014432d347a0d46139654c23f..d3e0590ea7a1be1ed70c115b4e5a1da997c50d46 100644 (file)
@@ -1023,8 +1023,6 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
                 if scope is None:
                     # Maybe it's a cimport.
                     scope = env.find_imported_module(self.module_path, self.pos)
-                    if scope:
-                        scope.fused_to_specific = env.fused_to_specific
             else:
                 scope = env
 
index a634aaf56738be000f652883189a2d3bee39d493..db497aa401419fdb98589e2838edaad19ee71a2d 100644 (file)
@@ -178,6 +178,7 @@ _directive_defaults = {
     'auto_pickle': None,
     'cdivision': False,  # was True before 0.12
     'cdivision_warnings': False,
+    'c_api_binop_methods': True,  # Change for 3.0
     'overflowcheck': False,
     'overflowcheck.fold': True,
     'always_allow_keywords': False,
index 1b38d8e917cc7c5ead5cdd57688517b728ca5a30..25453b39aba523598baaa2ed6da668868cb120a9 100644 (file)
@@ -69,7 +69,8 @@ cdef bint check_for_non_ascii_characters(unicode string)
 @cython.locals(systr=unicode, is_python3_source=bint, is_raw=bint)
 cdef p_string_literal(PyrexScanner s, kind_override=*)
 cdef _append_escape_sequence(kind, builder, unicode escape_sequence, PyrexScanner s)
-@cython.locals(i=Py_ssize_t, size=Py_ssize_t, c=Py_UCS4)
+cdef tuple _f_string_error_pos(pos, string, Py_ssize_t i)
+@cython.locals(i=Py_ssize_t, size=Py_ssize_t, c=Py_UCS4, next_start=Py_ssize_t)
 cdef list p_f_string(PyrexScanner s, unicode_value, pos, bint is_raw)
 @cython.locals(i=Py_ssize_t, size=Py_ssize_t, c=Py_UCS4, quote_char=Py_UCS4, NO_CHAR=Py_UCS4)
 cdef tuple p_f_string_expr(PyrexScanner s, unicode_value, pos, Py_ssize_t starting_index, bint is_raw)
index fd19d6c8e0882ddcb85951db225d5e37fc836d68..9b506687329c0391b14091cd13001f9873b33548 100644 (file)
@@ -882,6 +882,7 @@ def p_string_literal(s, kind_override=None):
     pos = s.position()
     is_python3_source = s.context.language_level >= 3
     has_non_ascii_literal_characters = False
+    string_start_pos = (pos[0], pos[1], pos[2] + len(s.systring))
     kind_string = s.systring.rstrip('"\'').lower()
     if len(kind_string) > 1:
         if len(set(kind_string)) != len(kind_string):
@@ -965,7 +966,7 @@ def p_string_literal(s, kind_override=None):
                 s.error("bytes can only contain ASCII literal characters.", pos=pos)
             bytes_value = None
     if kind == 'f':
-        unicode_value = p_f_string(s, unicode_value, pos, is_raw='r' in kind_string)
+        unicode_value = p_f_string(s, unicode_value, string_start_pos, is_raw='r' in kind_string)
     s.next()
     return (kind, bytes_value, unicode_value)
 
@@ -1037,6 +1038,10 @@ _parse_escape_sequences_raw, _parse_escape_sequences = [re.compile((
     for is_raw in (True, False)]
 
 
+def _f_string_error_pos(pos, string, i):
+    return (pos[0], pos[1], pos[2] + i + 1)  # FIXME: handle newlines in string
+
+
 def p_f_string(s, unicode_value, pos, is_raw):
     # Parses a PEP 498 f-string literal into a list of nodes. Nodes are either UnicodeNodes
     # or FormattedValueNodes.
@@ -1044,15 +1049,13 @@ def p_f_string(s, unicode_value, pos, is_raw):
     next_start = 0
     size = len(unicode_value)
     builder = StringEncoding.UnicodeLiteralBuilder()
-    error_pos = list(pos)  # [src, line, column]
     _parse_seq = _parse_escape_sequences_raw if is_raw else _parse_escape_sequences
 
     while next_start < size:
         end = next_start
-        error_pos[2] = pos[2] + end  # FIXME: handle newlines in string
         match = _parse_seq(unicode_value, next_start)
         if match is None:
-            error(tuple(error_pos), "Invalid escape sequence")
+            error(_f_string_error_pos(pos, unicode_value, next_start), "Invalid escape sequence")
 
         next_start = match.end()
         part = match.group()
@@ -1076,7 +1079,8 @@ def p_f_string(s, unicode_value, pos, is_raw):
             if part == '}}':
                 builder.append('}')
             else:
-                s.error("f-string: single '}' is not allowed", pos=tuple(error_pos))
+                error(_f_string_error_pos(pos, unicode_value, end),
+                      "f-string: single '}' is not allowed")
         else:
             builder.append(part)
 
@@ -1097,16 +1101,20 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
     nested_depth = 0
     quote_char = NO_CHAR
     in_triple_quotes = False
+    backslash_reported = False
 
     while True:
         if i >= size:
-            s.error("missing '}' in format string expression")
+            break  # error will be reported below
         c = unicode_value[i]
 
         if quote_char != NO_CHAR:
             if c == '\\':
-                error_pos = (pos[0], pos[1] + i, pos[2])  # FIXME: handle newlines in string
-                error(error_pos, "backslashes not allowed in f-strings")
+                # avoid redundant error reports along '\' sequences
+                if not backslash_reported:
+                    error(_f_string_error_pos(pos, unicode_value, i),
+                          "backslashes not allowed in f-strings")
+                backslash_reported = True
             elif c == quote_char:
                 if in_triple_quotes:
                     if i + 2 < size and unicode_value[i + 1] == c and unicode_value[i + 2] == c:
@@ -1125,7 +1133,8 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
         elif nested_depth != 0 and c in '}])':
             nested_depth -= 1
         elif c == '#':
-            s.error("format string cannot include #")
+            error(_f_string_error_pos(pos, unicode_value, i),
+                  "format string cannot include #")
         elif nested_depth == 0 and c in '!:}':
             # allow != as a special case
             if c == '!' and i + 1 < size and unicode_value[i + 1] == '=':
@@ -1141,12 +1150,13 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
     expr_pos = (pos[0], pos[1], pos[2] + starting_index + 2)  # TODO: find exact code position (concat, multi-line, ...)
 
     if not expr_str.strip():
-        error(expr_pos, "empty expression not allowed in f-string")
+        error(_f_string_error_pos(pos, unicode_value, starting_index),
+              "empty expression not allowed in f-string")
 
     if terminal_char == '!':
         i += 1
         if i + 2 > size:
-            error(expr_pos, "invalid conversion char at end of string")
+            pass  # error will be reported below
         else:
             conversion_char = unicode_value[i]
             i += 1
@@ -1159,7 +1169,7 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
         start_format_spec = i + 1
         while True:
             if i >= size:
-                s.error("missing '}' in format specifier", pos=expr_pos)
+                break  # error will be reported below
             c = unicode_value[i]
             if not in_triple_quotes and not in_string:
                 if c == '{':
@@ -1181,7 +1191,9 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
         format_spec_str = unicode_value[start_format_spec:i]
 
     if terminal_char != '}':
-        s.error("missing '}' in format string expression', found '%s'" % terminal_char)
+        error(_f_string_error_pos(pos, unicode_value, i),
+              "missing '}' in format string expression" + (
+                  ", found '%s'" % terminal_char if terminal_char else ""))
 
     # parse the expression as if it was surrounded by parentheses
     buf = StringIO('(%s)' % expr_str)
@@ -1190,7 +1202,7 @@ def p_f_string_expr(s, unicode_value, pos, starting_index, is_raw):
 
     # validate the conversion char
     if conversion_char is not None and not ExprNodes.FormattedValueNode.find_conversion_func(conversion_char):
-        error(pos, "invalid conversion character '%s'" % conversion_char)
+        error(expr_pos, "invalid conversion character '%s'" % conversion_char)
 
     # the format spec is itself treated like an f-string
     if format_spec_str:
index 137ea4eba80e918021636eb8a858c7f0c3ac6a46..50e09cd749a19c1f225223dfc90d4a9a26486c82 100644 (file)
@@ -180,13 +180,14 @@ class SlotDescriptor(object):
     #  ifdef                         Full #ifdef string that slot is wrapped in. Using this causes py3, py2 and flags to be ignored.)
 
     def __init__(self, slot_name, dynamic=False, inherited=False,
-                 py3=True, py2=True, ifdef=None):
+                 py3=True, py2=True, ifdef=None, is_binop=False):
         self.slot_name = slot_name
         self.is_initialised_dynamically = dynamic
         self.is_inherited = inherited
         self.ifdef = ifdef
         self.py3 = py3
         self.py2 = py2
+        self.is_binop = is_binop
 
     def preprocessor_guard_code(self):
         ifdef = self.ifdef
@@ -405,6 +406,17 @@ class SyntheticSlot(InternalMethodSlot):
             return self.default_value
 
 
+class BinopSlot(SyntheticSlot):
+    def __init__(self, signature, slot_name, left_method, **kargs):
+        assert left_method.startswith('__')
+        right_method = '__r' + left_method[2:]
+        SyntheticSlot.__init__(
+                self, slot_name, [left_method, right_method], "0", is_binop=True, **kargs)
+        # MethodSlot causes special method registration.
+        self.left_slot = MethodSlot(signature, "", left_method)
+        self.right_slot = MethodSlot(signature, "", right_method)
+
+
 class RichcmpSlot(MethodSlot):
     def slot_code(self, scope):
         entry = scope.lookup_here(self.method_name)
@@ -728,23 +740,23 @@ property_accessor_signatures = {
 PyNumberMethods_Py3_GUARD = "PY_MAJOR_VERSION < 3 || (CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03050000)"
 
 PyNumberMethods = (
-    MethodSlot(binaryfunc, "nb_add", "__add__"),
-    MethodSlot(binaryfunc, "nb_subtract", "__sub__"),
-    MethodSlot(binaryfunc, "nb_multiply", "__mul__"),
-    MethodSlot(binaryfunc, "nb_divide", "__div__", ifdef = PyNumberMethods_Py3_GUARD),
-    MethodSlot(binaryfunc, "nb_remainder", "__mod__"),
-    MethodSlot(binaryfunc, "nb_divmod", "__divmod__"),
-    MethodSlot(ternaryfunc, "nb_power", "__pow__"),
+    BinopSlot(binaryfunc, "nb_add", "__add__"),
+    BinopSlot(binaryfunc, "nb_subtract", "__sub__"),
+    BinopSlot(binaryfunc, "nb_multiply", "__mul__"),
+    BinopSlot(binaryfunc, "nb_divide", "__div__", ifdef = PyNumberMethods_Py3_GUARD),
+    BinopSlot(binaryfunc, "nb_remainder", "__mod__"),
+    BinopSlot(binaryfunc, "nb_divmod", "__divmod__"),
+    BinopSlot(ternaryfunc, "nb_power", "__pow__"),
     MethodSlot(unaryfunc, "nb_negative", "__neg__"),
     MethodSlot(unaryfunc, "nb_positive", "__pos__"),
     MethodSlot(unaryfunc, "nb_absolute", "__abs__"),
     MethodSlot(inquiry, "nb_nonzero", "__nonzero__", py3 = ("nb_bool", "__bool__")),
     MethodSlot(unaryfunc, "nb_invert", "__invert__"),
-    MethodSlot(binaryfunc, "nb_lshift", "__lshift__"),
-    MethodSlot(binaryfunc, "nb_rshift", "__rshift__"),
-    MethodSlot(binaryfunc, "nb_and", "__and__"),
-    MethodSlot(binaryfunc, "nb_xor", "__xor__"),
-    MethodSlot(binaryfunc, "nb_or", "__or__"),
+    BinopSlot(binaryfunc, "nb_lshift", "__lshift__"),
+    BinopSlot(binaryfunc, "nb_rshift", "__rshift__"),
+    BinopSlot(binaryfunc, "nb_and", "__and__"),
+    BinopSlot(binaryfunc, "nb_xor", "__xor__"),
+    BinopSlot(binaryfunc, "nb_or", "__or__"),
     EmptySlot("nb_coerce", ifdef = PyNumberMethods_Py3_GUARD),
     MethodSlot(unaryfunc, "nb_int", "__int__", fallback="__long__"),
     MethodSlot(unaryfunc, "nb_long", "__long__", fallback="__int__", py3 = "<RESERVED>"),
@@ -767,8 +779,8 @@ PyNumberMethods = (
 
     # Added in release 2.2
     # The following require the Py_TPFLAGS_HAVE_CLASS flag
-    MethodSlot(binaryfunc, "nb_floor_divide", "__floordiv__"),
-    MethodSlot(binaryfunc, "nb_true_divide", "__truediv__"),
+    BinopSlot(binaryfunc, "nb_floor_divide", "__floordiv__"),
+    BinopSlot(binaryfunc, "nb_true_divide", "__truediv__"),
     MethodSlot(ibinaryfunc, "nb_inplace_floor_divide", "__ifloordiv__"),
     MethodSlot(ibinaryfunc, "nb_inplace_true_divide", "__itruediv__"),
 
@@ -776,7 +788,7 @@ PyNumberMethods = (
     MethodSlot(unaryfunc, "nb_index", "__index__"),
 
     # Added in release 3.5
-    MethodSlot(binaryfunc, "nb_matrix_multiply", "__matmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
+    BinopSlot(binaryfunc, "nb_matrix_multiply", "__matmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
     MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
 )
 
index 5d8d628d5691eecb6540211ba6eb0f0be390ebfc..1e123fdb1fc2d468e93ea7367a8cd6618d6ee79f 100644 (file)
@@ -1,7 +1,7 @@
 # cython.* namespace for pure mode.
 from __future__ import absolute_import
 
-__version__ = "0.29.19"
+__version__ = "0.29.20"
 
 try:
     from __builtin__ import basestring
index 27a551213a2c997466ed778af12cb2074b820e86..3c0e49129cc00bdc0132fffab37cafc66ee43841 100644 (file)
@@ -279,7 +279,8 @@ static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
     {
         PyObject *copy = _PyLong_Copy((PyLongObject*)n);
         if (likely(copy)) {
-            Py_SIZE(copy) = -(Py_SIZE(copy));
+            // negate the size to swap the sign
+            __Pyx_SET_SIZE(copy, -Py_SIZE(copy));
         }
         return copy;
     }
index 8f2349722c4b4f40ac0a92276209ba59be34188f..a65c8eee361d1041b4c88831ebd69fd74c60bf73 100644 (file)
@@ -1292,7 +1292,8 @@ static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
     // special C-API function only in Pyston and PyPy >= 5.9
     if (PyMethodDescr_Check(method))
 #else
-    // It appears that PyMethodDescr_Type is not exposed anywhere in the CPython C-API
+    #if PY_MAJOR_VERSION == 2
+    // PyMethodDescr_Type is not exposed in the CPython C-API in Py2.
     static PyTypeObject *methoddescr_type = NULL;
     if (methoddescr_type == NULL) {
        PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append");
@@ -1300,6 +1301,9 @@ static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
        methoddescr_type = Py_TYPE(meth);
        Py_DECREF(meth);
     }
+    #else
+    PyTypeObject *methoddescr_type = &PyMethodDescr_Type;
+    #endif
     if (__Pyx_TypeCheck(method, methoddescr_type))
 #endif
     {
@@ -1317,16 +1321,7 @@ static PyObject* __Pyx_Method_ClassMethod(PyObject *method) {
         // python classes
         return PyClassMethod_New(PyMethod_GET_FUNCTION(method));
     }
-    else if (PyCFunction_Check(method)) {
-        return PyClassMethod_New(method);
-    }
-#ifdef __Pyx_CyFunction_USED
-    else if (__Pyx_CyFunction_Check(method)) {
+    else {
         return PyClassMethod_New(method);
     }
-#endif
-    PyErr_SetString(PyExc_TypeError,
-                   "Class-level classmethod() can only be called on "
-                   "a method_descriptor or instance method.");
-    return NULL;
 }
index 1b39c9e42d308f5675fd397000c1a04ffa789c8a..85b262bb2f95d797ee9a6a2c6850e59ae1b7ccbb 100644 (file)
@@ -278,3 +278,56 @@ __PYX_GOOD:
     Py_XDECREF(setstate_cython);
     return ret;
 }
+
+/////////////// BinopSlot ///////////////
+
+static CYTHON_INLINE PyObject *{{func_name}}_maybe_call_slot(PyTypeObject* type, PyObject *left, PyObject *right {{extra_arg_decl}}) {
+    {{slot_type}} slot;
+#if CYTHON_USE_TYPE_SLOTS
+    slot = type->tp_as_number ? type->tp_as_number->{{slot_name}} : NULL;
+#else
+    slot = ({{slot_type}}) PyType_GetSlot(type, Py_{{slot_name}});
+#endif
+    return slot ? slot(left, right {{extra_arg}}) : __Pyx_NewRef(Py_NotImplemented);
+}
+
+static PyObject *{{func_name}}(PyObject *left, PyObject *right {{extra_arg_decl}}) {
+    PyObject *res;
+    int maybe_self_is_left, maybe_self_is_right = 0;
+    maybe_self_is_left = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+            || (Py_TYPE(left)->tp_as_number && Py_TYPE(left)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+            || __Pyx_TypeCheck(left, {{type_cname}});
+    // Optimize for the common case where the left operation is defined (and successful).
+    if (!{{overloads_left}}) {
+        maybe_self_is_right = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+                || (Py_TYPE(right)->tp_as_number && Py_TYPE(right)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+                || __Pyx_TypeCheck(right, {{type_cname}});
+    }
+    if (maybe_self_is_left) {
+        if (maybe_self_is_right && !{{overloads_left}}) {
+            res = {{call_right}};
+            if (res != Py_NotImplemented) return res;
+            Py_DECREF(res);
+            // Don't bother calling it again.
+            maybe_self_is_right = 0;
+        }
+        res = {{call_left}};
+        if (res != Py_NotImplemented) return res;
+        Py_DECREF(res);
+    }
+    if ({{overloads_left}}) {
+        maybe_self_is_right = Py_TYPE(left) == Py_TYPE(right)
+#if CYTHON_USE_TYPE_SLOTS
+                || (Py_TYPE(right)->tp_as_number && Py_TYPE(right)->tp_as_number->{{slot_name}} == &{{func_name}})
+#endif
+                || PyType_IsSubtype(Py_TYPE(right), {{type_cname}});
+    }
+    if (maybe_self_is_right) {
+        return {{call_right}};
+    }
+    return __Pyx_NewRef(Py_NotImplemented);
+}
index 40901859b86603acca63075ee7e32216ac1019c7..1c3e63d252e2551a0855d6f49deb8e10f5b7b651 100644 (file)
@@ -609,6 +609,15 @@ static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) {
   #define PySet_CheckExact(obj)        (Py_TYPE(obj) == &PySet_Type)
 #endif
 
+
+#if PY_VERSION_HEX >= 0x030900A4
+  #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt)
+  #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size)
+#else
+  #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
+  #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size)
+#endif
+
 #if CYTHON_ASSUME_SAFE_MACROS
   #define __Pyx_PySequence_SIZE(seq)  Py_SIZE(seq)
 #else
index d6c32ace2033049e47a98b6ed5e0eb1cc263ae09..35f3a67c962d1542021ddcb9c0c1df92fff5c089 100644 (file)
@@ -35,7 +35,7 @@ static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) {
     if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) {
         Py_INCREF(x);
         PyList_SET_ITEM(list, len, x);
-        Py_SIZE(list) = len+1;
+        __Pyx_SET_SIZE(list, len + 1);
         return 0;
     }
     return PyList_Append(list, x);
@@ -53,7 +53,7 @@ static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) {
     if (likely(L->allocated > len)) {
         Py_INCREF(x);
         PyList_SET_ITEM(list, len, x);
-        Py_SIZE(list) = len+1;
+        __Pyx_SET_SIZE(list, len + 1);
         return 0;
     }
     return PyList_Append(list, x);
@@ -104,7 +104,7 @@ static CYTHON_INLINE PyObject* __Pyx__PyObject_Pop(PyObject* L) {
 static CYTHON_INLINE PyObject* __Pyx_PyList_Pop(PyObject* L) {
     /* Check that both the size is positive and no reallocation shrinking needs to be done. */
     if (likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) {
-        Py_SIZE(L) -= 1;
+        __Pyx_SET_SIZE(L, Py_SIZE(L) - 1);
         return PyList_GET_ITEM(L, PyList_GET_SIZE(L));
     }
     return CALL_UNBOUND_METHOD(PyList_Type, "pop", L);
@@ -167,7 +167,7 @@ static PyObject* __Pyx__PyList_PopIndex(PyObject* L, PyObject* py_ix, Py_ssize_t
         }
         if (likely(__Pyx_is_valid_index(cix, size))) {
             PyObject* v = PyList_GET_ITEM(L, cix);
-            Py_SIZE(L) -= 1;
+            __Pyx_SET_SIZE(L, Py_SIZE(L) - 1);
             size -= 1;
             memmove(&PyList_GET_ITEM(L, cix), &PyList_GET_ITEM(L, cix+1), (size_t)(size-cix)*sizeof(PyObject*));
             return v;
index c9ffdec35c25bd9a92b0602a331010519b43a12d..11672705eb0b27b13d69a85eff66609924f54c8f 100755 (executable)
@@ -617,6 +617,7 @@ class TestBuilder(object):
         self.cleanup_failures = options.cleanup_failures
         self.with_pyregr = with_pyregr
         self.cython_only = options.cython_only
+        self.doctest_selector = re.compile(options.only_pattern).search if options.only_pattern else None
         self.languages = languages
         self.test_bugs = test_bugs
         self.fork = options.fork
@@ -730,12 +731,14 @@ class TestBuilder(object):
         else:
             languages = self.languages
 
-        if skip_c(tags) and 'c' in languages:
+        if 'c' in languages and skip_c(tags):
             languages = list(languages)
             languages.remove('c')
-        elif 'no-cpp' in tags['tag'] and 'cpp' in self.languages:
+        if 'cpp' in languages and 'no-cpp' in tags['tag']:
             languages = list(languages)
             languages.remove('cpp')
+        if not languages:
+            return []
 
         pythran_dir = self.pythran_dir
         if 'pythran' in tags['tag'] and not pythran_dir and 'cpp' in languages:
@@ -772,6 +775,7 @@ class TestBuilder(object):
                           cleanup_sharedlibs=self.cleanup_sharedlibs,
                           cleanup_failures=self.cleanup_failures,
                           cython_only=self.cython_only,
+                          doctest_selector=self.doctest_selector,
                           fork=self.fork,
                           language_level=self.language_level,
                           warning_errors=warning_errors,
@@ -812,7 +816,7 @@ def filter_stderr(stderr_bytes):
 class CythonCompileTestCase(unittest.TestCase):
     def __init__(self, test_directory, workdir, module, tags, language='c', preparse='id',
                  expect_errors=False, expect_warnings=False, annotate=False, cleanup_workdir=True,
-                 cleanup_sharedlibs=True, cleanup_failures=True, cython_only=False,
+                 cleanup_sharedlibs=True, cleanup_failures=True, cython_only=False, doctest_selector=None,
                  fork=True, language_level=2, warning_errors=False,
                  test_determinism=False,
                  common_utility_dir=None, pythran_dir=None, stats=None):
@@ -830,6 +834,7 @@ class CythonCompileTestCase(unittest.TestCase):
         self.cleanup_sharedlibs = cleanup_sharedlibs
         self.cleanup_failures = cleanup_failures
         self.cython_only = cython_only
+        self.doctest_selector = doctest_selector
         self.fork = fork
         self.language_level = language_level
         self.warning_errors = warning_errors
@@ -1276,6 +1281,8 @@ class CythonRunTestCase(CythonCompileTestCase):
             else:
                 module = module_or_name
             tests = doctest.DocTestSuite(module)
+            if self.doctest_selector is not None:
+                tests._tests[:] = [test for test in tests._tests if self.doctest_selector(test.id())]
             with self.stats.time(self.name, self.language, 'run'):
                 tests.run(result)
         run_forked_test(result, run_test, self.shortDescription(), self.fork)
@@ -2058,6 +2065,8 @@ def main():
     parser.add_option("-T", "--ticket", dest="tickets",
                       action="append",
                       help="a bug ticket number to run the respective test in 'tests/*'")
+    parser.add_option("-k", dest="only_pattern",
+                      help="a regex pattern for selecting doctests and test functions in the test modules")
     parser.add_option("-3", dest="language_level",
                       action="store_const", const=3, default=2,
                       help="set language level to Python 3 (useful for running the CPython regression tests)'")
diff --git a/tests/errors/e_cpp_only_features.pyx b/tests/errors/e_cpp_only_features.pyx
new file mode 100644 (file)
index 0000000..005e415
--- /dev/null
@@ -0,0 +1,26 @@
+# mode: error
+# tag: no-cpp, werror
+
+from cython.operator import typeid
+
+def use_typeid():
+    cdef int i = 0
+    print typeid(i) == typeid(i)
+
+cdef cppclass A:
+    pass
+
+def use_new():
+    cdef A* x = new A()
+
+def use_del():
+    cdef A a = A()
+    cdef A *p = &a
+    del p
+
+_ERRORS = """
+8:10: typeid operator only allowed in c++
+8:23: typeid operator only allowed in c++
+14:20: Operation only allowed in c++
+19:4: Operation only allowed in c++
+"""
diff --git a/tests/errors/e_fstring.pyx b/tests/errors/e_fstring.pyx
new file mode 100644 (file)
index 0000000..05fae50
--- /dev/null
@@ -0,0 +1,23 @@
+# mode: error
+# tag: fstring
+
+def incorrect_fstrings(x):
+    return [
+        f"{x}{'\\'}'{x+1}",
+        f"""{}""",
+        f"{}",
+        f"{x!}",
+        f"{",
+        f"{{}}}",
+    ]
+
+
+_ERRORS = """
+6:16: backslashes not allowed in f-strings
+7:14: empty expression not allowed in f-string
+8:12: empty expression not allowed in f-string
+9:14: missing '}' in format string expression, found '!'
+10:12: empty expression not allowed in f-string
+10:12: missing '}' in format string expression
+11:15: f-string: single '}' is not allowed
+"""
index 59175e35bdcf36237d0e114aa7884d3843d7dbb8..c2046ce191b824e27ed1095b5073b36b1d0c4031 100644 (file)
@@ -112,6 +112,10 @@ def class_py3k_args():
     args = []
     kwargs = {}
 
+def uninitialized_in_sizeof():
+    cdef int i
+    print sizeof(i)
+
 _ERRORS = """
 6:10: local variable 'a' referenced before assignment
 12:11: local variable 'a' might be referenced before assignment
diff --git a/tests/errors/w_uninitialized_cpp.pyx b/tests/errors/w_uninitialized_cpp.pyx
new file mode 100644 (file)
index 0000000..ae35978
--- /dev/null
@@ -0,0 +1,12 @@
+# cython: warn.maybe_uninitialized=True
+# mode: error
+# tag: cpp, werror
+
+from cython.operator import typeid
+
+def uninitialized_in_typeid():
+    cdef int i
+    print typeid(i) == typeid(i)
+
+_ERRORS = """
+"""
index 13079a8fc7ee114f6443cb69dbc1f27c56812d9e..ba6351d9bbc7e8a175b57b107ff257e51dbce274 100644 (file)
@@ -1,5 +1,6 @@
 # mode: run
 # ticket: 698
+# distutils: extra_compile_args=-fwrapv
 
 cdef extern from *:
     int INT_MAX
index 9b095c5256ac87fe44387d5065bca6b27f522489..fdc03be7d5bffbfdce853f0febe04066dbcd5c5a 100644 (file)
@@ -9,6 +9,10 @@ class1
 class1
 >>> class1().bview()
 class1
+>>> class1().cview()
+class1
+>>> class1().cview("XX")
+class1XX
 
 >>> class2.view()
 class2
@@ -35,6 +39,12 @@ cimport cython
 def f_plus(cls, a):
     return cls.a + a
 
+def second_decorator(f):
+    # note - a class, not a function (didn't pass Cython's test in __Pyx_Method_ClassMethod)
+    class C:
+        def __call__(self, *args):
+            return f(*args)
+    return C()
 
 class class1:
     a = 5
@@ -48,6 +58,11 @@ class class1:
     def bview(cls):
         print cls.__name__
 
+    @classmethod
+    @second_decorator
+    def cview(cls, s=""):
+        print cls.__name__+s
+
 
 class class2(object):
     a = 6
index 335a2cfe86182ead262d5d4f2b7012ac89c85777..cbe0797fd959d44f98a45a6bf436f5ca21e3c707 100644 (file)
@@ -258,6 +258,32 @@ def except_as_deletes_target_in_gen(x, a):
         yield 6
 
 
+def nested_except_gh3666(a=False, b=False):
+    """
+    >>> print(nested_except_gh3666())
+    A
+    >>> print(nested_except_gh3666(a=True))
+    B-V
+    >>> print(nested_except_gh3666(a=True, b=True))
+    B-V-T
+    """
+    try:
+        if a:
+            raise ValueError
+        return "A"
+    except TypeError as exc:
+        return "A-T"
+    except ValueError as exc:
+        try:
+            if b:
+                raise TypeError
+            return "B-V"
+        except ValueError as exc:
+            return "B-V-V"
+        except TypeError as exc:
+            return "B-V-T"
+
+
 ### Py3 feature tests
 
 def print_function(*args):
index f7493a79f82d90cf98bea521750c0889f506ea7a..851f3fbd9e65df9bb94219f1aa09cf172079c543 100644 (file)
@@ -400,3 +400,27 @@ def test_composite(fused_composite x):
         return x
     else:
         return 2 * x
+
+
+### see GH3642 - presence of cdef inside "unrelated" caused a type to be incorrectly inferred
+cdef unrelated(cython.floating x):
+    cdef cython.floating t = 1
+    return t
+
+cdef handle_float(float* x): return 'float'
+
+cdef handle_double(double* x): return 'double'
+
+def convert_to_ptr(cython.floating x):
+    """
+    >>> convert_to_ptr(1.0)
+    'double'
+    >>> convert_to_ptr['double'](1.0)
+    'double'
+    >>> convert_to_ptr['float'](1.0)
+    'float'
+    """
+    if cython.floating is float:
+        return handle_float(&x)
+    elif cython.floating is double:
+        return handle_double(&x)
index bb27285869401335990944ef8061fc4c36fdc312..c8b66402564a1b88d5e5af1bc6231a56bd619fbe 100644 (file)
@@ -25,9 +25,9 @@ cdef class C:
 
 cdef extern from "Python.h":
     """
-    #define Py_SET_SIZE(obj, size)  Py_SIZE((obj)) = (size)
+    #define my_SET_SIZE(obj, size)  __Pyx_SET_SIZE(obj, size)
     """
-    void Py_SET_SIZE(object, Py_ssize_t)
+    void my_SET_SIZE(object, Py_ssize_t)
 
 
 def test_square(x):
@@ -59,4 +59,4 @@ def test_class():
 def test_set_size(x, size):
     # This function manipulates Python objects in a bad way, so we
     # do not call it. The real test is that it compiles.
-    Py_SET_SIZE(x, size)
+    my_SET_SIZE(x, size)