Imported Upstream version 0.29.22 upstream/0.29.22
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 13 Jul 2022 06:25:07 +0000 (15:25 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 13 Jul 2022 06:25:07 +0000 (15:25 +0900)
37 files changed:
.gitrev
CHANGES.rst
Cython/Compiler/Buffer.py
Cython/Compiler/Builtin.py
Cython/Compiler/ExprNodes.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py
Cython/Compiler/PyrexTypes.py
Cython/Includes/cpython/array.pxd
Cython/Includes/cpython/buffer.pxd
Cython/Includes/cpython/cellobject.pxd [new file with mode: 0644]
Cython/Includes/cpython/codecs.pxd [new file with mode: 0644]
Cython/Includes/cpython/conversion.pxd [new file with mode: 0644]
Cython/Includes/cpython/pycapsule.pxd
Cython/Includes/numpy/__init__.pxd
Cython/Includes/posix/fcntl.pxd
Cython/Shadow.py
Cython/Utility/Coroutine.c
Cython/Utility/Embed.c
Cython/Utility/MemoryView.pyx
Cython/Utility/ObjectHandling.c
Cython/Utility/TypeConversion.c
Cython/Utility/arrayarray.h
Cython/Utils.py
PKG-INFO
docs/src/userguide/language_basics.rst
runtests.py
tests/build/common_include_dir.srctree
tests/compile/cpp_templates_nested.pyx [new file with mode: 0644]
tests/errors/cpdef_vars.pyx [new file with mode: 0644]
tests/memoryview/memslice.pyx
tests/run/cimport.srctree
tests/run/constants.pyx [moved from tests/run/consts.pyx with 100% similarity]
tests/run/cython3.pyx
tests/run/cython_includes.pyx
tests/run/fused_types.pyx

diff --git a/.gitrev b/.gitrev
index 9852a62..f4c0d7e 100644 (file)
--- a/.gitrev
+++ b/.gitrev
@@ -1 +1 @@
-976f5483c6df8570f34076ef25af7e7512dd9347
+fc777dd6f2661e9b34f50cbd39ac9b184eded484
index 96ae40c..bb73c43 100644 (file)
@@ -2,6 +2,63 @@
 Cython Changelog
 ================
 
+0.29.22 (2021-02-20)
+====================
+
+Features added
+--------------
+
+* Some declarations were added to the provided pxd includes.
+  Patches by Zackery Spytz and John Kirkham.
+  (Github issues #3811, #3882, #3899, #3901)
+
+Bugs fixed
+----------
+
+* A crash when calling certain functions in Py3.9 and later was resolved.
+  (Github issue #3917)
+
+* ``const`` memory views of structs failed to compile.
+  (Github issue #2251)
+
+* ``const`` template declarations could not be nested.
+  Patch by Ashwin Srinath.  (Github issue #1355)
+
+* The declarations in the ``cpython.pycapsule`` module were missing their
+  ``const`` modifiers and generated incorrect C code.
+  Patch by Warren Weckesser.  (Github issue #3964)
+
+* Casts to memory views failed for fused dtypes.
+  Patch by David Woods.  (Github issue #3881)
+
+* ``repr()`` was assumed to return ``str`` instead of ``unicode`` with ``language_level=3``.
+  (Github issue #3736)
+
+* Calling ``cpdef`` functions from cimported modules crashed the compiler.
+  Patch by David Woods.  (Github issue #4000)
+
+* Cython no longer validates the ABI size of the NumPy classes it compiled against.
+  See the discussion in https://github.com/numpy/numpy/pull/432
+
+* A C compiler warning about enum value casting was resolved in GCC.
+  (Github issue #2749)
+
+* Coverage reporting in the annotated HTML file failed in Py3.9.
+  Patch by Nick Pope.  (Github issue #3865)
+
+* The embedding code now reports Python errors as exit status.
+
+* Long type declarations could lead to (harmless) random changes in the
+  C file when used in auto-generated Python wrappers or pickled classes.
+
+Other changes
+-------------
+
+* Variables defined as ``cpdef`` now generate a warning since this
+  is currently useless and thus does not do what users would expect.
+  Patch by David Woods.  (Github issue #3959)
+
+
 0.29.21 (2020-07-09)
 ====================
 
index 04385b4..c62a24f 100644 (file)
@@ -668,9 +668,11 @@ def get_type_information_cname(code, dtype, maxdepth=None):
         if dtype.is_simple_buffer_dtype():
             structinfo_name = "NULL"
         elif dtype.is_struct:
-            fields = dtype.scope.var_entries
-            # Must pre-call all used types in order not to recurse utility code
-            # writing.
+            struct_scope = dtype.scope
+            if dtype.is_const:
+                struct_scope = struct_scope.const_base_type_scope
+            # Must pre-call all used types in order not to recurse during utility code writing.
+            fields = struct_scope.var_entries
             assert len(fields) > 0
             types = [get_type_information_cname(code, f.type, maxdepth - 1)
                      for f in fields]
index 1bdc643..5fa7175 100644 (file)
@@ -203,7 +203,7 @@ builtin_function_table = [
     #('raw_input', "",     "",      ""),
     #('reduce',    "",     "",      ""),
     BuiltinFunction('reload',     "O",    "O",     "PyImport_ReloadModule"),
-    BuiltinFunction('repr',       "O",    "O",     "PyObject_Repr", builtin_return_type='str'),
+    BuiltinFunction('repr',       "O",    "O",     "PyObject_Repr"),  # , builtin_return_type='str'),  # add in Cython 3.1
     #('round',     "",     "",      ""),
     BuiltinFunction('setattr',    "OOO",  "r",     "PyObject_SetAttr"),
     #('sum',       "",     "",      ""),
index 831575b..86bfa21 100644 (file)
@@ -6049,7 +6049,7 @@ class PyMethodCallNode(SimpleCallNode):
             # not an attribute itself, but might have been assigned from one (e.g. bound method)
             for assignment in self.function.cf_state:
                 value = assignment.rhs
-                if value and value.is_attribute and value.obj.type.is_pyobject:
+                if value and value.is_attribute and value.obj.type and value.obj.type.is_pyobject:
                     if attribute_is_likely_method(value):
                         likely_method = 'likely'
                         break
@@ -6851,7 +6851,11 @@ class AttributeNode(ExprNode):
         # FIXME: this is way too redundant with analyse_types()
         node = self.analyse_as_cimported_attribute_node(env, target=False)
         if node is not None:
-            return node.entry.type
+            if node.entry.type and node.entry.type.is_cfunction:
+                # special-case - function converted to pointer
+                return PyrexTypes.CPtrType(node.entry.type)
+            else:
+                return node.entry.type
         node = self.analyse_as_type_attribute(env)
         if node is not None:
             return node.entry.type
index 9733ea7..3f0956d 100644 (file)
@@ -429,7 +429,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
             except ImportError:
                 import xml.etree.ElementTree as ET
             coverage_xml = ET.parse(coverage_xml_filename).getroot()
-            for el in coverage_xml.getiterator():
+            if hasattr(coverage_xml, 'iter'):
+                iterator = coverage_xml.iter()  # Python 2.7 & 3.2+
+            else:
+                iterator = coverage_xml.getiterator()
+            for el in iterator:
                 el.tail = None  # save some memory
         else:
             coverage_xml = None
index dd79bd5..fe55903 100644 (file)
@@ -1048,6 +1048,8 @@ class CSimpleBaseTypeNode(CBaseTypeNode):
                         type = PyrexTypes.TemplatePlaceholderType(self.name)
                     else:
                         error(self.pos, "'%s' is not a type identifier" % self.name)
+        if type and type.is_fused and env.fused_to_specific:
+            type = type.specialize(env.fused_to_specific)
         if self.complex:
             if not type.is_numeric or type.is_complex:
                 error(self.pos, "can only complexify c numeric types")
@@ -1380,6 +1382,9 @@ class CVarDefNode(StatNode):
                     self.entry.type.create_to_py_utility_code(env)
                     self.entry.create_wrapper = True
             else:
+                if self.overridable:
+                    warning(self.pos, "cpdef variables will not be supported in Cython 3; "
+                            "currently they are no different from cdef variables", 2)
                 if self.directive_locals:
                     error(self.pos, "Decorators can only be followed by functions")
                 self.entry = dest_scope.declare_var(
index 9b50668..82bf82d 100644 (file)
@@ -2678,7 +2678,7 @@ def looking_at_expr(s):
             s.put_back(*saved)
         elif s.sy == '[':
             s.next()
-            is_type = s.sy == ']'
+            is_type = s.sy == ']' or not looking_at_expr(s)  # could be a nested template type
             s.put_back(*saved)
 
         dotted_path.reverse()
index b998d35..dcb51fe 100644 (file)
@@ -4,8 +4,8 @@
 
 from __future__ import absolute_import
 
-import collections
 import copy
+import hashlib
 import re
 
 try:
@@ -4731,5 +4731,5 @@ def type_identifier(type):
 def cap_length(s, max_prefix=63, max_len=1024):
     if len(s) <= max_prefix:
         return s
-    else:
-        return '%x__%s__etc' % (abs(hash(s)) % (1<<20), s[:max_len-17])
+    hash_prefix = hashlib.sha256(s.encode('ascii')).hexdigest()[:6]
+    return '%s__%s__etc' % (hash_prefix, s[:max_len-17])
index 3d2bb05..19230a0 100644 (file)
@@ -131,14 +131,14 @@ cdef inline array clone(array template, Py_ssize_t length, bint zero):
     """ fast creation of a new array, given a template array.
     type will be same as template.
     if zero is true, new array will be initialized with zeroes."""
-    op = newarrayobject(Py_TYPE(template), length, template.ob_descr)
+    cdef array op = newarrayobject(Py_TYPE(template), length, template.ob_descr)
     if zero and op is not None:
         memset(op.data.as_chars, 0, length * op.ob_descr.itemsize)
     return op
 
 cdef inline array copy(array self):
     """ make a copy of an array. """
-    op = newarrayobject(Py_TYPE(self), Py_SIZE(self), self.ob_descr)
+    cdef array op = newarrayobject(Py_TYPE(self), Py_SIZE(self), self.ob_descr)
     memcpy(op.data.as_chars, self.data.as_chars, Py_SIZE(op) * op.ob_descr.itemsize)
     return op
 
index 30b83d7..3f1ada7 100644 (file)
@@ -3,6 +3,9 @@
 cdef extern from "Python.h":
 
     cdef enum:
+        PyBUF_MAX_NDIM
+
+    cdef enum:
         PyBUF_SIMPLE,
         PyBUF_WRITABLE,
         PyBUF_WRITEABLE, # backwards compatibility
diff --git a/Cython/Includes/cpython/cellobject.pxd b/Cython/Includes/cpython/cellobject.pxd
new file mode 100644 (file)
index 0000000..5e3dd3d
--- /dev/null
@@ -0,0 +1,35 @@
+from .object cimport PyObject
+
+cdef extern from "Python.h":
+
+    ############################################################################
+    # Cell Objects
+    ############################################################################
+
+    bint PyCell_Check(object ob)
+    # Return true if ob is a cell object; ob must not be NULL.
+
+    object PyCell_New(PyObject* ob)
+    # Return value: New reference.
+    # Create and return a new cell object containing the value ob. The
+    # parameter may be NULL.
+
+    object PyCell_Get(object cell)
+    # Return value: New reference.
+    # Return the contents of the cell object cell.
+
+    object PyCell_GET(object cell)
+    # Return value: Borrowed reference.
+    # Return the contents of the cell object cell, but without checking that
+    # cell is non-NULL and is a cell object.
+
+    int PyCell_Set(object cell, PyObject* value) except? -1
+    # Set the contents of the cell object cell to value. This releases the
+    # reference to any current content of the cell. value may be NULL. cell
+    # must be non-NULL; if it is not a cell object, -1 will be returned. On
+    # success, 0 will be returned.
+
+    void PyCell_SET(object cell, PyObject* value)
+    # Sets the value of the cell object cell to value. No reference counts are
+    # adjusted, and no checks are made for safety; cell must be non-NULL and
+    # must be a cell object.
diff --git a/Cython/Includes/cpython/codecs.pxd b/Cython/Includes/cpython/codecs.pxd
new file mode 100644 (file)
index 0000000..f2ca7d2
--- /dev/null
@@ -0,0 +1,121 @@
+cdef extern from "Python.h":
+
+    ###########################################################################
+    # Codec registry and support functions
+    ###########################################################################
+
+    int PyCodec_Register(object search_function)
+    # Register a new codec search function.
+
+    # As side effect, this tries to load the encodings package, if not yet
+    # done, to make sure that it is always first in the list of search
+    # functions.
+
+    int PyCodec_KnownEncoding(const char *encoding)
+    # Return 1 or 0 depending on whether there is a registered codec for the
+    # given encoding. This function always succeeds.
+
+    object PyCodec_Encode(object o, const char *encoding, const char *errors)
+    # Return value: New reference.
+    # Generic codec based encoding API.
+
+    # o is passed through the encoder function found for the given encoding
+    # using the error handling method defined by errors. errors may be NULL
+    # to use the default method defined for the codec. Raises a LookupError
+    # if no encoder can be found.
+
+    object PyCodec_Decode(object o, const char *encoding, const char *errors)
+    # Return value: New reference.
+    # Generic codec based decoding API.
+
+    # o is passed through the decoder function found for the given encoding
+    # using the error handling method defined by errors. errors may be NULL
+    # to use the default method defined for the codec. Raises a LookupError
+    # if no encoder can be found.
+
+
+    # Codec lookup API
+
+    # In the following functions, the encoding string is looked up converted
+    # to all lower-case characters, which makes encodings looked up through
+    # this mechanism effectively case-insensitive. If no codec is found, a
+    # KeyError is set and NULL returned.
+
+    object PyCodec_Encoder(const char *encoding)
+    # Return value: New reference.
+    # Get an encoder function for the given encoding.
+
+    object PyCodec_Decoder(const char *encoding)
+    # Return value: New reference.
+    # Get a decoder function for the given encoding.
+
+    object PyCodec_IncrementalEncoder(const char *encoding, const char *errors)
+    # Return value: New reference.
+    # Get an IncrementalEncoder object for the given encoding.
+
+    object PyCodec_IncrementalDecoder(const char *encoding, const char *errors)
+    # Return value: New reference.
+    # Get an IncrementalDecoder object for the given encoding.
+
+    object PyCodec_StreamReader(const char *encoding, object stream, const char *errors)
+    # Return value: New reference.
+    # Get a StreamReader factory function for the given encoding.
+
+    object PyCodec_StreamWriter(const char *encoding, object stream, const char *errors)
+    # Return value: New reference.
+    # Get a StreamWriter factory function for the given encoding.
+
+
+    # Registry API for Unicode encoding error handlers
+
+    int PyCodec_RegisterError(const char *name, object error) except? -1
+    # Register the error handling callback function error under the given
+    # name. This callback function will be called by a codec when it
+    # encounters unencodable characters/undecodable bytes and name is
+    # specified as the error parameter in the call to the encode/decode
+    # function.
+
+    # The callback gets a single argument, an instance of
+    # UnicodeEncodeError, UnicodeDecodeError or UnicodeTranslateError that
+    # holds information about the problematic sequence of characters or bytes
+    # and their offset in the original string (see Unicode Exception Objects
+    # for functions to extract this information). The callback must either
+    # raise the given exception, or return a two-item tuple containing the
+    # replacement for the problematic sequence, and an integer giving the
+    # offset in the original string at which encoding/decoding should be
+    # resumed.
+
+    # Return 0 on success, -1 on error.
+
+    object PyCodec_LookupError(const char *name)
+    # Return value: New reference.
+    # Lookup the error handling callback function registered under name. As a
+    # special case NULL can be passed, in which case the error handling
+    # callback for "strict" will be returned.
+
+    object PyCodec_StrictErrors(object exc)
+    # Return value: Always NULL.
+    # Raise exc as an exception.
+
+    object PyCodec_IgnoreErrors(object exc)
+    # Return value: New reference.
+    # Ignore the unicode error, skipping the faulty input.
+
+    object PyCodec_ReplaceErrors(object exc)
+    # Return value: New reference.
+    # Replace the unicode encode error with "?" or "U+FFFD".
+
+    object PyCodec_XMLCharRefReplaceErrors(object exc)
+    # Return value: New reference.
+    # Replace the unicode encode error with XML character references.
+
+    object PyCodec_BackslashReplaceErrors(object exc)
+    # Return value: New reference.
+    # Replace the unicode encode error with backslash escapes ("\x", "\u"
+    # and "\U").
+
+    object PyCodec_NameReplaceErrors(object exc)
+    # Return value: New reference.
+    # Replace the unicode encode error with "\N{...}" escapes.
+
+    # New in version 3.5.
diff --git a/Cython/Includes/cpython/conversion.pxd b/Cython/Includes/cpython/conversion.pxd
new file mode 100644 (file)
index 0000000..18e2c3d
--- /dev/null
@@ -0,0 +1,36 @@
+# From https://docs.python.org/3/c-api/conversion.html
+
+from .object cimport PyObject
+
+cdef extern from "Python.h":
+    ctypedef struct va_list
+
+    int PyOS_snprintf(char *str, size_t size, const char *format, ...)
+    # Output not more than size bytes to str according to the format
+    # string format and the extra arguments. See the Unix man page snprintf(2).
+
+    int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va)
+    # Output not more than size bytes to str according to the format
+    # string format and the variable argument list va. Unix man page vsnprintf(2).
+
+    double PyOS_string_to_double(const char *s, char **endptr, PyObject *overflow_exception) except? -1.0
+    # Convert a string s to a double, raising a Python exception on failure. The set of
+    # accepted strings corresponds to the set of strings accepted by Python’s float()
+    # constructor, except that s must not have leading or trailing whitespace.
+    # The conversion is independent of the current locale.
+
+    enum:
+        Py_DTSF_SIGN
+        Py_DTSF_ADD_DOT_0
+        Py_DTSF_ALT
+
+    char* PyOS_double_to_string(double val, char format_code, int precision, int flags, int *ptype) except NULL
+    # Convert a double val to a string using supplied format_code, precision, and flags.
+
+    int PyOS_stricmp(const char *s1, const char *s2)
+    # Case insensitive comparison of strings. The function works almost identically
+    # to strcmp() except that it ignores the case.
+
+    int PyOS_strnicmp(const char *s1, const char *s2, Py_ssize_t  size)
+    # Case insensitive comparison of strings. The function works almost identically
+    # to strncmp() except that it ignores the case.
index 449f369..c3d12c7 100644 (file)
@@ -1,7 +1,6 @@
 
 # available since Python 3.1!
 
-# note all char* in the below functions are actually const char*
 
 cdef extern from "Python.h":
 
@@ -25,7 +24,7 @@ cdef extern from "Python.h":
     # Return true if its argument is a PyCapsule.
 
 
-    object PyCapsule_New(void *pointer, char *name,
+    object PyCapsule_New(void *pointer, const char *name,
                          PyCapsule_Destructor destructor)
     # Return value: New reference.
     #
@@ -48,7 +47,7 @@ cdef extern from "Python.h":
     # PyCapsule_Import().
 
 
-    void* PyCapsule_GetPointer(object capsule, char *name) except? NULL
+    void* PyCapsule_GetPointer(object capsule, const char *name) except? NULL
     # Retrieve the pointer stored in the capsule. On failure, set an
     # exception and return NULL.
     #
@@ -67,7 +66,7 @@ cdef extern from "Python.h":
     # or PyErr_Occurred() to disambiguate.
 
 
-    char* PyCapsule_GetName(object capsule) except? NULL
+    const char* PyCapsule_GetName(object capsule) except? NULL
     # Return the current name stored in the capsule. On failure, set
     # an exception and return NULL.
     #
@@ -85,7 +84,7 @@ cdef extern from "Python.h":
     # PyErr_Occurred() to disambiguate.
 
 
-    bint PyCapsule_IsValid(object capsule, char *name)
+    bint PyCapsule_IsValid(object capsule, const char *name)
     # Determines whether or not capsule is a valid capsule. A valid
     # capsule is non-NULL, passes PyCapsule_CheckExact(), has a
     # non-NULL pointer stored in it, and its internal name matches the
@@ -115,7 +114,7 @@ cdef extern from "Python.h":
     # failure.
 
 
-    int PyCapsule_SetName(object capsule, char *name) except -1
+    int PyCapsule_SetName(object capsule, const char *name) except -1
     # Set the name inside capsule to name. If non-NULL, the name must
     # outlive the capsule. If the previous name stored in the capsule
     # was not NULL, no attempt is made to free it.
@@ -129,7 +128,7 @@ cdef extern from "Python.h":
     # success. Return nonzero and set an exception on failure.
 
 
-    void* PyCapsule_Import(char *name, int no_block) except? NULL
+    void* PyCapsule_Import(const char *name, int no_block) except? NULL
     # Import a pointer to a C object from a capsule attribute in a
     # module. The name parameter should specify the full name to the
     # attribute, as in module.attribute. The name stored in the
index 15c0702..15700c0 100644 (file)
@@ -226,11 +226,11 @@ cdef extern from "numpy/arrayobject.h":
         # this field via the inline helper method PyDataType_SHAPE.
         cdef PyArray_ArrayDescr* subarray
 
-    ctypedef extern class numpy.flatiter [object PyArrayIterObject]:
+    ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]:
         # Use through macros
         pass
 
-    ctypedef extern class numpy.broadcast [object PyArrayMultiIterObject]:
+    ctypedef class numpy.broadcast [object PyArrayMultiIterObject, check_size ignore]:
         # Use through macros
         pass
 
@@ -914,7 +914,7 @@ cdef extern from "numpy/ufuncobject.h":
 
     ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *)
 
-    ctypedef extern class numpy.ufunc [object PyUFuncObject]:
+    ctypedef class numpy.ufunc [object PyUFuncObject, check_size ignore]:
         cdef:
             int nin, nout, nargs
             int identity
index 768fcdb..9afc33a 100644 (file)
@@ -24,6 +24,7 @@ cdef extern from "<fcntl.h>" nogil:
     enum: SEEK_END
 
     enum: O_CREAT
+    enum: O_DIRECT
     enum: O_EXCL
     enum: O_NOCTTY
     enum: O_TRUNC
index dab8c49..c1b5075 100644 (file)
@@ -1,7 +1,7 @@
 # cython.* namespace for pure mode.
 from __future__ import absolute_import
 
-__version__ = "0.29.21"
+__version__ = "0.29.22"
 
 try:
     from __builtin__ import basestring
index abdf4f3..e5b4ce6 100644 (file)
@@ -788,6 +788,32 @@ PyObject *__Pyx_Coroutine_MethodReturn(CYTHON_UNUSED PyObject* gen, PyObject *re
     return retval;
 }
 
+#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
+static CYTHON_INLINE
+PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) {
+#if PY_VERSION_HEX <= 0x030A00A1
+    return _PyGen_Send(gen, arg);
+#else
+    PyObject *result;
+    // PyIter_Send() asserts non-NULL arg
+    if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) {
+        if (PyAsyncGen_CheckExact(gen)) {
+            assert(result == Py_None);
+            PyErr_SetNone(PyExc_StopAsyncIteration);
+        }
+        else if (result == Py_None) {
+            PyErr_SetNone(PyExc_StopIteration);
+        }
+        else {
+            _PyGen_SetStopIterationValue(result);
+        }
+        Py_CLEAR(result);
+    }
+    return result;
+#endif
+}
+#endif
+
 static CYTHON_INLINE
 PyObject *__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen) {
     PyObject *ret;
@@ -829,13 +855,13 @@ static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) {
         #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
         // _PyGen_Send() is not exported before Py3.6
         if (PyGen_CheckExact(yf)) {
-            ret = _PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+            ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
         } else
         #endif
         #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03050000 && defined(PyCoro_CheckExact) && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
         // _PyGen_Send() is not exported before Py3.6
         if (PyCoro_CheckExact(yf)) {
-            ret = _PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
+            ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value);
         } else
         #endif
         {
@@ -931,7 +957,7 @@ static PyObject *__Pyx_Generator_Next(PyObject *self) {
         #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3)
         // _PyGen_Send() is not exported before Py3.6
         if (PyGen_CheckExact(yf)) {
-            ret = _PyGen_Send((PyGenObject*)yf, NULL);
+            ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL);
         } else
         #endif
         #ifdef __Pyx_Coroutine_USED
@@ -1165,7 +1191,7 @@ static void __Pyx_Coroutine_del(PyObject *self) {
 #if !CYTHON_USE_TP_FINALIZE
     // Temporarily resurrect the object.
     assert(self->ob_refcnt == 0);
-    self->ob_refcnt = 1;
+    __Pyx_SET_REFCNT(self, 1);
 #endif
 
     __Pyx_PyThreadState_assign
@@ -1257,7 +1283,7 @@ static void __Pyx_Coroutine_del(PyObject *self) {
     {
         Py_ssize_t refcnt = self->ob_refcnt;
         _Py_NewReference(self);
-        self->ob_refcnt = refcnt;
+        __Pyx_SET_REFCNT(self, refcnt);
     }
 #if CYTHON_COMPILING_IN_CPYTHON
     assert(PyType_IS_GC(self->ob_type) &&
index 8b8c6c8..8f7e8f4 100644 (file)
@@ -58,7 +58,12 @@ static int __Pyx_main(int argc, wchar_t **argv) {
       }
       Py_XDECREF(m);
     }
+#if PY_VERSION_HEX < 0x03060000
     Py_Finalize();
+#else
+    if (Py_FinalizeEx() < 0)
+        return 2;
+#endif
     return 0;
 }
 
@@ -200,7 +205,11 @@ int
         if (res == 0)
             res = __Pyx_main(argc, argv_copy);
         for (i = 0; i < argc; i++) {
+#if PY_VERSION_HEX < 0x03050000
             free(argv_copy2[i]);
+#else
+            PyMem_RawFree(argv_copy2[i]);
+#endif
         }
         free(argv_copy);
         free(argv_copy2);
index 3c92d5d..6ca5fab 100644 (file)
@@ -1466,7 +1466,8 @@ cdef bytes format_from_typeinfo(__Pyx_TypeInfo *type):
     cdef bytes part, result
 
     if type.typegroup == 'S':
-        assert type.fields != NULL and type.fields.type != NULL
+        assert type.fields != NULL
+        assert type.fields.type != NULL
 
         if type.flags & __PYX_BUF_FLAGS_PACKED_STRUCT:
             alignment = b'^'
index fbb22be..2d42909 100644 (file)
@@ -2271,7 +2271,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObjec
             // fast and simple case that we are optimising for
             return __Pyx_PyObject_CallMethO(func, arg);
 #if CYTHON_FAST_PYCCALL
-        } else if (PyCFunction_GET_FLAGS(func) & METH_FASTCALL) {
+        } else if (__Pyx_PyFastCFunction_Check(func)) {
             return __Pyx_PyCFunction_FastCall(func, &arg, 1);
 #endif
         }
index 1983457..42e4744 100644 (file)
@@ -430,6 +430,15 @@ static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
 }
 
 
+/////////////// GCCDiagnostics.proto ///////////////
+
+// GCC diagnostic pragmas were introduced in GCC 4.6
+// Used to silence conversion warnings that are ok but cannot be avoided.
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#define __Pyx_HAS_GCC_DIAGNOSTIC
+#endif
+
+
 /////////////// ToPyCTupleUtility.proto ///////////////
 static PyObject* {{funcname}}({{struct_type_decl}});
 
@@ -616,9 +625,17 @@ static CYTHON_INLINE Py_UNICODE __Pyx_PyObject_AsPy_UNICODE(PyObject* x) {
 static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value);
 
 /////////////// CIntToPy ///////////////
+//@requires: GCCDiagnostics
 
 static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value) {
-    const {{TYPE}} neg_one = ({{TYPE}}) (({{TYPE}}) 0 - ({{TYPE}}) 1), const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+    const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
     const int is_unsigned = neg_one > const_zero;
     if (is_unsigned) {
         if (sizeof({{TYPE}}) < sizeof(long)) {
@@ -687,6 +704,7 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t wid
 /////////////// CIntToPyUnicode ///////////////
 //@requires: StringTools.c::BuildPyUnicode
 //@requires: CIntToDigits
+//@requires: GCCDiagnostics
 
 #ifdef _MSC_VER
     #ifndef _MSC_STDINT_H_
@@ -702,10 +720,6 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t wid
 
 // NOTE: inlining because most arguments are constant, which collapses lots of code below
 
-// GCC diagnostic pragmas were introduced in GCC 4.6
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
-#define GCC_DIAGNOSTIC
-#endif
 static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t width, char padding_char, char format_char) {
     // simple and conservative C string allocation on the stack: each byte gives at most 3 digits, plus sign
     char digits[sizeof({{TYPE}})*3+2];
@@ -715,12 +729,12 @@ static CYTHON_INLINE PyObject* {{TO_PY_FUNCTION}}({{TYPE}} value, Py_ssize_t wid
     Py_ssize_t length, ulength;
     int prepend_sign, last_one_off;
     {{TYPE}} remaining;
-#ifdef GCC_DIAGNOSTIC
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wconversion"
 #endif
     const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
-#ifdef GCC_DIAGNOSTIC
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
 #pragma GCC diagnostic pop
 #endif
     const int is_unsigned = neg_one > const_zero;
@@ -847,11 +861,19 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *);
 
 /////////////// CIntFromPy ///////////////
 //@requires: CIntFromPyVerify
+//@requires: GCCDiagnostics
 
 {{py: from Cython.Utility import pylong_join }}
 
 static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
-    const {{TYPE}} neg_one = ({{TYPE}}) (({{TYPE}}) 0 - ({{TYPE}}) 1), const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+    const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = ({{TYPE}}) 0;
+#ifdef __Pyx_HAS_GCC_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
     const int is_unsigned = neg_one > const_zero;
 #if PY_MAJOR_VERSION < 3
     if (likely(PyInt_Check(x))) {
index 61a883f..a9e4923 100644 (file)
@@ -88,7 +88,7 @@ static CYTHON_INLINE PyObject * newarrayobject(PyTypeObject *type, Py_ssize_t si
     op->ob_descr = descr;
     op->allocated = size;
     op->weakreflist = NULL;
-    op->ob_size = size;
+    __Pyx_SET_SIZE(op, size);
     if (size <= 0) {
         op->data.ob_item = NULL;
     }
@@ -116,7 +116,7 @@ static CYTHON_INLINE int resize(arrayobject *self, Py_ssize_t n) {
         return -1;
     }
     self->data.ob_item = (char*) items;
-    self->ob_size = n;
+    __Pyx_SET_SIZE(self, n);
     self->allocated = n;
     return 0;
 }
@@ -126,7 +126,7 @@ static CYTHON_INLINE int resize_smart(arrayobject *self, Py_ssize_t n) {
     void *items = (void*) self->data.ob_item;
     Py_ssize_t newsize;
     if (n < self->allocated && n*4 > self->allocated) {
-        self->ob_size = n;
+        __Pyx_SET_SIZE(self, n);
         return 0;
     }
     newsize = n + (n / 2) + 1;
@@ -140,7 +140,7 @@ static CYTHON_INLINE int resize_smart(arrayobject *self, Py_ssize_t n) {
         return -1;
     }
     self->data.ob_item = (char*) items;
-    self->ob_size = n;
+    __Pyx_SET_SIZE(self, n);
     self->allocated = newsize;
     return 0;
 }
index 83b2988..d59d67d 100644 (file)
@@ -21,6 +21,7 @@ import re
 import io
 import codecs
 import shutil
+import tempfile
 from contextlib import contextmanager
 
 modification_time = os.path.getmtime
@@ -334,41 +335,25 @@ def get_cython_cache_dir():
 
 @contextmanager
 def captured_fd(stream=2, encoding=None):
-    pipe_in = t = None
     orig_stream = os.dup(stream)  # keep copy of original stream
     try:
-        pipe_in, pipe_out = os.pipe()
-        os.dup2(pipe_out, stream)  # replace stream by copy of pipe
-        try:
-            os.close(pipe_out)  # close original pipe-out stream
-            data = []
-
-            def copy():
-                try:
-                    while True:
-                        d = os.read(pipe_in, 1000)
-                        if d:
-                            data.append(d)
-                        else:
-                            break
-                finally:
-                    os.close(pipe_in)
-
-            def get_output():
-                output = b''.join(data)
-                if encoding:
-                    output = output.decode(encoding)
-                return output
-
-            from threading import Thread
-            t = Thread(target=copy)
-            t.daemon = True  # just in case
-            t.start()
-            yield get_output
-        finally:
-            os.dup2(orig_stream, stream)  # restore original stream
-            if t is not None:
-                t.join()
+        with tempfile.TemporaryFile(mode="a+b") as temp_file:
+            def read_output(_output=[b'']):
+                if not temp_file.closed:
+                    temp_file.seek(0)
+                    _output[0] = temp_file.read()
+                return _output[0]
+
+            os.dup2(temp_file.fileno(), stream)  # replace stream by copy of pipe
+            try:
+                def get_output():
+                    result = read_output()
+                    return result.decode(encoding) if encoding else result
+
+                yield get_output
+            finally:
+                os.dup2(orig_stream, stream)  # restore original stream
+                read_output()  # keep the output in case it's used after closing the context manager
     finally:
         os.close(orig_stream)
 
index e3bee73..9977f87 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.2
 Name: Cython
-Version: 0.29.21
+Version: 0.29.22
 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.
index b5746e3..0fdd877 100644 (file)
@@ -355,27 +355,40 @@ of :ref:`error_return_values`.
 Error return values
 -------------------
 
-If you don't do anything special, a function declared with :keyword:`cdef` that
-does not return a Python object has no way of reporting Python exceptions to
-its caller. If an exception is detected in such a function, a warning message
-is printed and the exception is ignored.
-
-If you want a C function that does not return a Python object to be able to
-propagate exceptions to its caller, you need to declare an exception value for
-it. Here is an example::
+In Python (more specifically, in the CPython runtime), exceptions that occur
+inside of a function are signaled to the caller and propagated up the call stack
+through defined error return values.  For functions that return a Python object
+(and thus, a pointer to such an object), the error return value is simply the
+``NULL`` pointer, so any function returning a Python object has a well-defined
+error return value.
+
+While this is always the case for :keyword:`def` functions, functions
+defined as :keyword:`cdef` or :keyword:`cpdef` can return arbitrary C types,
+which do not have such a well-defined error return value.  Thus, if an
+exception is detected in such a function, a warning message is printed,
+the exception is ignored, and the function returns immediately without
+propagating the exception to its caller.
+
+If you want such a C function to be able to propagate exceptions, you need
+to declare an exception return value for it as a contract with the caller.
+Here is an example::
 
     cdef int spam() except -1:
         ...
 
-With this declaration, whenever an exception occurs inside spam, it will
-immediately return with the value ``-1``. Furthermore, whenever a call to spam
-returns ``-1``, an exception will be assumed to have occurred and will be
-propagated.
+With this declaration, whenever an exception occurs inside ``spam``, it will
+immediately return with the value ``-1``.  From the caller's side, whenever
+a call to spam returns ``-1``, the caller will assume that an exception has
+occurred and can now process or propagate it.
 
-When you declare an exception value for a function, you should never
-explicitly or implicitly return that value. In particular, if the exceptional return value
-is a ``False`` value, then you should ensure the function will never terminate via an implicit
-or empty return.
+When you declare an exception value for a function, you should never explicitly
+or implicitly return that value.  This includes empty :keyword:`return`
+statements, without a return value, for which Cython inserts the default return
+value (e.g. ``0`` for C number types).  In general, exception return values
+are best chosen from invalid or very unlikely return values of the function,
+such as a negative value for functions that return only non-negative results,
+or a very large value like ``INT_MAX`` for a function that "usually" only
+returns small results.
 
 If all possible return values are legal and you
 can't reserve one entirely for signalling errors, you can use an alternative
@@ -384,9 +397,10 @@ form of exception value declaration::
     cdef int spam() except? -1:
         ...
 
-The "?" indicates that the value ``-1`` only indicates a possible error. In this
-case, Cython generates a call to :c:func:`PyErr_Occurred` if the exception value is
-returned, to make sure it really is an error.
+The "?" indicates that the value ``-1`` only signals a possible error. In this
+case, Cython generates a call to :c:func:`PyErr_Occurred` if the exception value
+is returned, to make sure it really received an exception and not just a normal
+result.
 
 There is also a third form of exception value declaration::
 
@@ -394,10 +408,11 @@ There is also a third form of exception value declaration::
         ...
 
 This form causes Cython to generate a call to :c:func:`PyErr_Occurred` after
-every call to spam, regardless of what value it returns. If you have a
-function returning void that needs to propagate errors, you will have to use
-this form, since there isn't any return value to test.
-Otherwise there is little use for this form.
+*every* call to spam, regardless of what value it returns. If you have a
+function returning ``void`` that needs to propagate errors, you will have to
+use this form, since there isn't any error return value to test.
+Otherwise, an explicit error return value allows the C compiler to generate
+more efficient code and is thus generally preferable.
 
 An external C++ function that may raise an exception can be declared with::
 
@@ -407,22 +422,22 @@ See :ref:`wrapping-cplusplus` for more details.
 
 Some things to note:
 
-* Exception values can only declared for functions returning an integer, enum,
-  float or pointer type, and the value must be a constant expression.
-  Void functions can only use the ``except *`` form.
+* Exception values can only be declared for functions returning a C integer,
+  enum, float or pointer type, and the value must be a constant expression.
+  Functions that return ``void``, or a struct/union by value, can only use
+  the ``except *`` form.
 * The exception value specification is part of the signature of the function.
   If you're passing a pointer to a function as a parameter or assigning it
   to a variable, the declared type of the parameter or variable must have
-  the same exception value specification (or lack thereof). Here is an
-  example of a pointer-to-function declaration with an exception
-  value::
+  the same exception value specification (or lack thereof).  Here is an
+  example of a pointer-to-function declaration with an exception value::
 
       int (*grail)(int, char*) except -1
 
 * You don't need to (and shouldn't) declare exception values for functions
   which return Python objects. Remember that a function with no declared
-  return type implicitly returns a Python object. (Exceptions on such functions
-  are implicitly propagated by returning NULL.)
+  return type implicitly returns a Python object. (Exceptions on such
+  functions are implicitly propagated by returning ``NULL``.)
 
 
 .. _checking_return_values_of_non_cython_functions:
@@ -922,4 +937,3 @@ The expressions in the ``IF`` and ``ELIF`` clauses must be valid compile-time
 expressions as for the ``DEF`` statement, although they can evaluate to any
 Python value, and the truth of the result is determined in the usual Python
 way.
-
index b7bb315..9b92361 100755 (executable)
@@ -642,7 +642,9 @@ class TestBuilder(object):
                     continue
                 suite.addTest(
                     self.handle_directory(path, filename))
-        if sys.platform not in ['win32'] and self.add_embedded_test:
+        if (sys.platform not in ['win32'] and self.add_embedded_test
+                # the embedding test is currently broken in Py3.8+, except on Linux.
+                and (sys.version_info < (3, 8) or sys.platform != 'darwin')):
             # Non-Windows makefile.
             if [1 for selector in self.selectors if selector("embedded")] \
                 and not [1 for selector in self.exclude_selectors if selector("embedded")]:
index 8867c46..0b3e4f3 100644 (file)
@@ -16,13 +16,18 @@ PYTHON fake_grep.py -c '#include "common/AddTraceback_impl_.*h"' c.c
 
 import sys
 from Cython.Build.Dependencies import cythonize
+import platform
+import os
 
 from distutils.core import setup
 
+# os x on Travis specifically seems to crash with nthreads>0
+osx_on_travis = (platform.system() == "Darwin" and os.getenv("TRAVIS"))
+
 # Test concurrent safety if multiprocessing is available.
 # (In particular, TravisCI does not support spawning processes from tests.)
 nthreads = 0
-if not hasattr(sys, 'pypy_version_info'):
+if not (hasattr(sys, 'pypy_version_info') or osx_on_travis):
     try:
         import multiprocessing
         multiprocessing.Pool(2).close()
diff --git a/tests/compile/cpp_templates_nested.pyx b/tests/compile/cpp_templates_nested.pyx
new file mode 100644 (file)
index 0000000..3466389
--- /dev/null
@@ -0,0 +1,18 @@
+# tag: cpp
+# mode: compile
+
+from libcpp.vector cimport vector
+
+cdef extern from *:
+    cdef cppclass Foo[T]:
+        pass
+
+    cdef cppclass Bar:
+        pass
+
+cdef vector[vector[int]] a
+cdef vector[vector[const int]] b
+cdef vector[vector[vector[int]]] c
+cdef vector[vector[vector[const int]]] d
+cdef Foo[Foo[Bar]] e
+cdef Foo[Foo[const Bar]] f
diff --git a/tests/errors/cpdef_vars.pyx b/tests/errors/cpdef_vars.pyx
new file mode 100644 (file)
index 0000000..f356dec
--- /dev/null
@@ -0,0 +1,24 @@
+# tag: warnings
+
+cpdef str a = "123"
+cpdef b = 2
+
+cdef class C:
+    cpdef float c
+
+def func():
+    """
+    >>> c = func()
+    >>> isinstance(c, C) or c
+    True
+    """
+    cpdef d = C()
+    return d
+
+
+_WARNINGS = """
+3:6: cpdef variables will not be supported in Cython 3; currently they are no different from cdef variables
+4:6: cpdef variables will not be supported in Cython 3; currently they are no different from cdef variables
+7:10: cpdef variables will not be supported in Cython 3; currently they are no different from cdef variables
+15:10: cpdef variables will not be supported in Cython 3; currently they are no different from cdef variables
+"""
index 4d280b8..827012c 100644 (file)
@@ -1155,9 +1155,21 @@ def basic_struct(MyStruct[:] buf):
     """
     See also buffmt.pyx
 
-    >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)]))  # , writable=False))
+    >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
     1 2 3 4 5
-    >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ccqii"))  # , writable=False))
+    >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ccqii"))
+    1 2 3 4 5
+    """
+    print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e
+
+@testcase
+def const_struct(const MyStruct[:] buf):
+    """
+    See also buffmt.pyx
+
+    >>> const_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
+    1 2 3 4 5
+    >>> const_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ccqii", writable=False))
     1 2 3 4 5
     """
     print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e
@@ -1167,9 +1179,21 @@ def nested_struct(NestedStruct[:] buf):
     """
     See also buffmt.pyx
 
-    >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))  # , writable=False))
+    >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
+    1 2 3 4 5
+    >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i"))
+    1 2 3 4 5
+    """
+    print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z
+
+@testcase
+def const_nested_struct(const NestedStruct[:] buf):
+    """
+    See also buffmt.pyx
+
+    >>> const_nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
     1 2 3 4 5
-    >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i"))  # , writable=False))
+    >>> const_nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i", writable=False))
     1 2 3 4 5
     """
     print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z
@@ -1179,11 +1203,26 @@ def packed_struct(PackedStruct[:] buf):
     """
     See also buffmt.pyx
 
-    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)]))  # , writable=False))
+    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)]))
+    1 2
+    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}"))
+    1 2
+    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}"))
+    1 2
+
+    """
+    print buf[0].a, buf[0].b
+
+@testcase
+def const_packed_struct(const PackedStruct[:] buf):
+    """
+    See also buffmt.pyx
+
+    >>> const_packed_struct(PackedStructMockBuffer(None, [(1, 2)], writable=False))
     1 2
-    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}"))  # , writable=False))
+    >>> const_packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}", writable=False))
     1 2
-    >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}"))  # , writable=False))
+    >>> const_packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}", writable=False))
     1 2
 
     """
@@ -1194,11 +1233,26 @@ def nested_packed_struct(NestedPackedStruct[:] buf):
     """
     See also buffmt.pyx
 
-    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))  # , writable=False))
+    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
+    1 2 3 4 5
+    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i"))
+    1 2 3 4 5
+    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i"))
+    1 2 3 4 5
+    """
+    print buf[0].a, buf[0].b, buf[0].sub.a, buf[0].sub.b, buf[0].c
+
+
+@testcase
+def const_nested_packed_struct(const NestedPackedStruct[:] buf):
+    """
+    See also buffmt.pyx
+
+    >>> const_nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
     1 2 3 4 5
-    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i"))  # , writable=False))
+    >>> const_nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i", writable=False))
     1 2 3 4 5
-    >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i"))  # , writable=False))
+    >>> const_nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i", writable=False))
     1 2 3 4 5
     """
     print buf[0].a, buf[0].b, buf[0].sub.a, buf[0].sub.b, buf[0].c
index 23b4a49..9811dd5 100644 (file)
@@ -45,12 +45,20 @@ from other cimport (
     A,
     foo,
 )
-print A, foo(10)
+print(A, foo(10))
 
 cimport other
-print other.A, other.foo(10)
+
+cdef call_fooptr(int (*fptr)(int)):
+    return fptr(10)
+
+def call_other_foo():
+    x = other.foo  # GH4000 - failed because other was untyped
+    return call_fooptr(x) # check that x is correctly resolved as a function pointer
+
+print(other.A, other.foo(10), call_other_foo())
 
 from pkg cimport sub
 cdef sub.my_int a = 100
 
-from pkg.subpkg cimport submod
\ No newline at end of file
+from pkg.subpkg cimport submod
similarity index 100%
rename from tests/run/consts.pyx
rename to tests/run/constants.pyx
index cbe0797..81e4ea6 100644 (file)
@@ -630,3 +630,13 @@ async def async_def_annotations(x: 'int') -> 'float':
     int
     """
     return float(x)
+
+
+'''
+def repr_returns_str(x) -> str:
+    """
+    >>> repr_returns_str(123)
+    '123'
+    """
+    return repr(x)
+'''
index f46ddb2..5aab63f 100644 (file)
@@ -10,9 +10,12 @@ cimport cpython.bool
 cimport cpython.buffer
 cimport cpython.bytearray
 cimport cpython.bytes
+cimport cpython.cellobject
 cimport cpython.ceval
 cimport cpython.cobject
+cimport cpython.codecs
 cimport cpython.complex
+cimport cpython.conversion
 cimport cpython.datetime
 cimport cpython.dict
 cimport cpython.exc
index 851f3fb..4e96bab 100644 (file)
@@ -326,6 +326,8 @@ def test_fused_memslice_dtype(cython.floating[:] array):
     cdef cython.floating[:] otherarray = array[0:100:1]
     print cython.typeof(array), cython.typeof(otherarray), \
           array[5], otherarray[6]
+    cdef cython.floating value;
+    cdef cython.floating[:] test_cast = <cython.floating[:1:1]>&value
 
 def test_fused_memslice_dtype_repeated(cython.floating[:] array1, cython.floating[:] array2):
     """