Imported Upstream version 0.29.11 upstream/0.29.11
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 03:10:24 +0000 (12:10 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 03:10:24 +0000 (12:10 +0900)
14 files changed:
CHANGES.rst
Cython/Build/Dependencies.py
Cython/Compiler/Main.py
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/Pythran.py
Cython/Debugger/Tests/TestLibCython.py
Cython/Shadow.py
Cython/Utility/ModuleSetupCode.c
Cython/Utility/Optimize.c
Cython/Utils.py
runtests.py
tests/compile/find_pxd.srctree
tests/run/closure_in_derived_class_T2967.pyx [new file with mode: 0644]
tests/run/language_level.srctree

index 1595106..7f37af2 100644 (file)
@@ -2,6 +2,35 @@
 Cython Changelog
 ================
 
+0.29.11 (2019-06-30)
+====================
+
+Bugs fixed
+----------
+
+* Fix compile error in CPython 3.8b2.
+  Patch by Nick Coghlan. (Github issue #3009)
+
+* Invalid C code generated for lambda functions in cdef methods.
+  Patch by Josh Tobin.  (Github issue #2967)
+
+* Support slice handling in newer Pythran versions.
+  Patch by Serge Guelton.  (Github issue #2989)
+
+* A reference leak in power-of-2 calculation was fixed.
+  Patch by Sebastian Berg.  (Github issue #3022)
+
+* The search order for include files was changed. Previously it was
+  ``include_directories``, ``Cython/Includes``, ``sys.path``. Now it is
+  ``include_directories``, ``sys.path``, ``Cython/Includes``. This was done to
+  allow third-party ``*.pxd`` files to override the ones in Cython.
+  Original patch by Matti Picus.  (Github issue #2905)
+
+* Setting ``language_level=2`` in a file did not work if ``language_level=3``
+  was enabled globally before.
+  Patch by Jeroen Demeyer.  (Github issue #2791)
+
+
 0.29.10 (2019-06-02)
 ====================
 
index fbd44e7..a4e5c60 100644 (file)
@@ -34,10 +34,8 @@ except ImportError:
 
 try:
     import pythran
-    import pythran.config
-    pythran_version = pythran.__version__
 except:
-    pythran_version = None
+    pythran = None
 
 from .. import Utils
 from ..Utils import (cached_function, cached_method, path_exists,
@@ -125,13 +123,13 @@ def file_hash(filename):
 
 
 def update_pythran_extension(ext):
-    if not pythran_version:
+    if pythran is None:
         raise RuntimeError("You first need to install Pythran to use the np_pythran directive.")
-    pythran_ext = (
-        pythran.config.make_extension(python=True)
-        if pythran_version >= '0.9' or pythran_version >= '0.8.7'
-        else pythran.config.make_extension()
-    )
+    try:
+        pythran_ext = pythran.config.make_extension(python=True)
+    except TypeError:  # older pythran version only
+        pythran_ext = pythran.config.make_extension()
+
     ext.include_dirs.extend(pythran_ext['include_dirs'])
     ext.extra_compile_args.extend(pythran_ext['extra_compile_args'])
     ext.extra_link_args.extend(pythran_ext['extra_link_args'])
@@ -947,8 +945,9 @@ def cythonize(module_list, exclude=None, nthreads=0, aliases=None, quiet=False,
     if 'common_utility_include_dir' in options:
         safe_makedirs(options['common_utility_include_dir'])
 
-    pythran_options = None
-    if pythran_version:
+    if pythran is None:
+        pythran_options = None
+    else:
         pythran_options = CompilationOptions(**options)
         pythran_options.cplus = True
         pythran_options.np_pythran = True
index 0f1ff4d..dc4add5 100644 (file)
@@ -38,6 +38,8 @@ module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_
 
 verbose = 0
 
+standard_include_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                        os.path.pardir, 'Includes'))
 
 class CompilationData(object):
     #  Bundles the information that is passed from transform to transform.
@@ -88,10 +90,6 @@ class Context(object):
         self.pxds = {}  # full name -> node tree
         self._interned = {}  # (type(value), value, *key_args) -> interned_value
 
-        standard_include_path = os.path.abspath(os.path.normpath(
-            os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
-        self.include_directories = include_directories + [standard_include_path]
-
         if language_level is not None:
             self.set_language_level(language_level)
 
@@ -99,18 +97,17 @@ class Context(object):
 
     def set_language_level(self, level):
         from .Future import print_function, unicode_literals, absolute_import, division
-        future_directives = []
+        future_directives = set()
         if level == '3str':
-            future_directives = [print_function, absolute_import, division]
-            self.future_directives.discard(unicode_literals)
             level = 3
         else:
             level = int(level)
             if level >= 3:
-                future_directives = [print_function, unicode_literals, absolute_import, division]
+                future_directives.add(unicode_literals)
+        if level >= 3:
+            future_directives.update([print_function, absolute_import, division])
         self.language_level = level
-        if future_directives:
-            self.future_directives.update(future_directives)
+        self.future_directives = future_directives
         if level >= 3:
             self.modules['builtins'] = self.modules['__builtin__']
 
@@ -290,8 +287,13 @@ class Context(object):
 
     def search_include_directories(self, qualified_name, suffix, pos,
                                    include=False, sys_path=False):
-        return Utils.search_include_directories(
-            tuple(self.include_directories), qualified_name, suffix, pos, include, sys_path)
+        include_dirs = self.include_directories
+        if sys_path:
+            include_dirs = include_dirs + sys.path
+        # include_dirs must be hashable for caching in @cached_function
+        include_dirs = tuple(include_dirs + [standard_include_path])
+        return search_include_directories(include_dirs, qualified_name,
+                                          suffix, pos, include)
 
     def find_root_package_dir(self, file_path):
         return Utils.find_root_package_dir(file_path)
@@ -778,6 +780,56 @@ def compile(source, options = None, full_module_name = None, **kwds):
         return compile_multiple(source, options)
 
 
+@Utils.cached_function
+def search_include_directories(dirs, qualified_name, suffix, pos, include=False):
+    """
+    Search the list of include directories for the given file name.
+
+    If a source file position is given, first searches the directory
+    containing that file. Returns None if not found, but does not
+    report an error.
+
+    The 'include' option will disable package dereferencing.
+    """
+
+    if pos:
+        file_desc = pos[0]
+        if not isinstance(file_desc, FileSourceDescriptor):
+            raise RuntimeError("Only file sources for code supported")
+        if include:
+            dirs = (os.path.dirname(file_desc.filename),) + dirs
+        else:
+            dirs = (Utils.find_root_package_dir(file_desc.filename),) + dirs
+
+    dotted_filename = qualified_name
+    if suffix:
+        dotted_filename += suffix
+
+    if not include:
+        names = qualified_name.split('.')
+        package_names = tuple(names[:-1])
+        module_name = names[-1]
+        module_filename = module_name + suffix
+        package_filename = "__init__" + suffix
+
+    for dirname in dirs:
+        path = os.path.join(dirname, dotted_filename)
+        if os.path.exists(path):
+            return path
+
+        if not include:
+            package_dir = Utils.check_package_dir(dirname, package_names)
+            if package_dir is not None:
+                path = os.path.join(package_dir, module_filename)
+                if os.path.exists(path):
+                    return path
+                path = os.path.join(package_dir, module_name,
+                                    package_filename)
+                if os.path.exists(path):
+                    return path
+    return None
+
+
 # ------------------------------------------------------------------------
 #
 #  Main command-line entry point
index 4ff717b..5ba64b4 100644 (file)
@@ -2715,9 +2715,12 @@ class CreateClosureClasses(CythonTransform):
             node.needs_outer_scope = True
             return
 
+        # entry.cname can contain periods (eg. a derived C method of a class).
+        # We want to use the cname as part of a C struct name, so we replace
+        # periods with double underscores.
         as_name = '%s_%s' % (
             target_module_scope.next_id(Naming.closure_class_prefix),
-            node.entry.cname)
+            node.entry.cname.replace('.','__'))
 
         entry = target_module_scope.declare_c_class(
             name=as_name, pos=node.pos, defining=True,
index fc81956..7114d70 100644 (file)
@@ -8,11 +8,10 @@ import cython
 
 try:
     import pythran
-    pythran_version = pythran.__version__
-    pythran_is_0_8_7 = pythran_version >= '0.9' or pythran_version >= '0.8.7'
+    pythran_is_pre_0_9 = tuple(map(int, pythran.__version__.split('.')[0:2])) < (0, 9)
 except ImportError:
-    pythran_version = None
-    pythran_is_0_8_7 = False
+    pythran = None
+    pythran_is_pre_0_9 = True
 
 
 # Pythran/Numpy specific operations
@@ -41,10 +40,10 @@ def pythran_type(Ty, ptype="ndarray"):
             ctype = dtype.typedef_cname
         else:
             raise ValueError("unsupported type %s!" % dtype)
-        if pythran_is_0_8_7:
-            return "pythonic::types::%s<%s,pythonic::types::pshape<%s>>" % (ptype,ctype, ",".join(("Py_ssize_t",)*ndim))
-        else:
+        if pythran_is_pre_0_9:
             return "pythonic::types::%s<%s,%d>" % (ptype,ctype, ndim)
+        else:
+            return "pythonic::types::%s<%s,pythonic::types::pshape<%s>>" % (ptype,ctype, ",".join(("Py_ssize_t",)*ndim))
     if Ty.is_pythran_expr:
         return Ty.pythran_type
     #if Ty.is_none:
@@ -82,14 +81,8 @@ def _index_access(index_code, indices):
 def _index_type_code(index_with_type):
     idx, index_type = index_with_type
     if idx.is_slice:
-        if idx.step.is_none:
-            func = "contiguous_slice"
-            n = 2
-        else:
-            func = "slice"
-            n = 3
-        return "pythonic::types::%s(%s)" % (
-            func, ",".join(["0"]*n))
+        n = 2 + int(not idx.step.is_none)
+        return "pythonic::__builtin__::functor::slice{}(%s)" % (",".join(["0"]*n))
     elif index_type.is_int:
         return "std::declval<%s>()" % index_type.sign_and_name()
     elif index_type.is_pythran_expr:
@@ -129,7 +122,10 @@ def np_func_to_list(func):
         return []
     return np_func_to_list(func.obj) + [func.attribute]
 
-if pythran_version:
+if pythran is None:
+    def pythran_is_numpy_func_supported(name):
+        return False
+else:
     def pythran_is_numpy_func_supported(func):
         CurF = pythran.tables.MODULES['numpy']
         FL = np_func_to_list(func)
@@ -138,9 +134,6 @@ if pythran_version:
             if CurF is None:
                 return False
         return True
-else:
-    def pythran_is_numpy_func_supported(name):
-        return False
 
 def pythran_functor(func):
     func = np_func_to_list(func)
@@ -214,6 +207,7 @@ def include_pythran_generic(env):
     env.add_include_file("pythonic/types/bool.hpp")
     env.add_include_file("pythonic/types/ndarray.hpp")
     env.add_include_file("pythonic/numpy/power.hpp")
+    env.add_include_file("pythonic/__builtin__/slice.hpp")
     env.add_include_file("<new>")  # for placement new
 
     for i in (8, 16, 32, 64):
index b789b10..1356064 100644 (file)
@@ -40,7 +40,7 @@ def test_gdb():
     else:
         stdout, _ = p.communicate()
         # Based on Lib/test/test_gdb.py
-        regex = "GNU gdb [^\d]*(\d+)\.(\d+)"
+        regex = r"GNU gdb [^\d]*(\d+)\.(\d+)"
         gdb_version = re.match(regex, stdout.decode('ascii', 'ignore'))
 
     if gdb_version:
index 08ebfb4..c8820ea 100644 (file)
@@ -1,7 +1,7 @@
 # cython.* namespace for pure mode.
 from __future__ import absolute_import
 
-__version__ = "0.29.10"
+__version__ = "0.29.11"
 
 try:
     from __builtin__ import basestring
index 6360215..5df5584 100644 (file)
@@ -384,6 +384,9 @@ class __Pyx_FakeReference {
 #if PY_VERSION_HEX < 0x030800A4
   #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
           PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+#elif PY_VERSION_HEX >= 0x030800B2
+  #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
+          PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
 #else
   #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
           PyCode_New(a, 0, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
index 22d1634..d6c32ac 100644 (file)
@@ -685,9 +685,11 @@ static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject
             return PyLong_FromUnsignedLongLong(value);
 #endif
         } else {
-            PyObject *one = PyInt_FromLong(1L);
+            PyObject *result, *one = PyInt_FromLong(1L);
             if (unlikely(!one)) return NULL;
-            return PyNumber_Lshift(one, exp);
+            result = PyNumber_Lshift(one, exp);
+            Py_DECREF(one);
+            return result;
         }
     } else if (shiftby == -1 && PyErr_Occurred()) {
         PyErr_Clear();
index 6caf410..380ad4a 100644 (file)
@@ -124,54 +124,6 @@ def copy_file_to_dir_if_newer(sourcefile, destdir):
 
 
 @cached_function
-def search_include_directories(dirs, qualified_name, suffix, pos,
-                               include=False, sys_path=False):
-    # Search the list of include directories for the given
-    # file name. If a source file position is given, first
-    # searches the directory containing that file. Returns
-    # None if not found, but does not report an error.
-    # The 'include' option will disable package dereferencing.
-    # If 'sys_path' is True, also search sys.path.
-    if sys_path:
-        dirs = dirs + tuple(sys.path)
-    if pos:
-        file_desc = pos[0]
-        from Cython.Compiler.Scanning import FileSourceDescriptor
-        if not isinstance(file_desc, FileSourceDescriptor):
-            raise RuntimeError("Only file sources for code supported")
-        if include:
-            dirs = (os.path.dirname(file_desc.filename),) + dirs
-        else:
-            dirs = (find_root_package_dir(file_desc.filename),) + dirs
-
-    dotted_filename = qualified_name
-    if suffix:
-        dotted_filename += suffix
-    if not include:
-        names = qualified_name.split('.')
-        package_names = tuple(names[:-1])
-        module_name = names[-1]
-        module_filename = module_name + suffix
-        package_filename = "__init__" + suffix
-
-    for dir in dirs:
-        path = os.path.join(dir, dotted_filename)
-        if path_exists(path):
-            return path
-        if not include:
-            package_dir = check_package_dir(dir, package_names)
-            if package_dir is not None:
-                path = os.path.join(package_dir, module_filename)
-                if path_exists(path):
-                    return path
-                path = os.path.join(dir, package_dir, module_name,
-                                    package_filename)
-                if path_exists(path):
-                    return path
-    return None
-
-
-@cached_function
 def find_root_package_dir(file_path):
     dir = os.path.dirname(file_path)
     if file_path == dir:
index 646df74..d37f988 100755 (executable)
@@ -758,12 +758,10 @@ class TestBuilder(object):
         pythran_dir = self.pythran_dir
         if 'pythran' in tags['tag'] and not pythran_dir and 'cpp' in languages:
             import pythran.config
-            from pythran import __version__ as pythran_version
-            pythran_ext = (
-                pythran.config.make_extension(python=True)
-                if pythran_version >= '0.9' or pythran_version >= '0.8.7'
-                else pythran.config.make_extension()
-            )
+            try:
+                pythran_ext = pythran.config.make_extension(python=True)
+            except TypeError:  # old pythran version syntax
+                pythran_ext = pythran.config.make_extension()
             pythran_dir = pythran_ext['include_dirs'][0]
 
         preparse_list = tags.get('preparse', ['id'])
index 1d7885f..75d2765 100644 (file)
@@ -6,12 +6,13 @@ from Cython.Build import cythonize
 from Cython.Distutils.extension import Extension
 
 import sys
-sys.path.append("path")
+sys.path.insert(0, "path")
 
 ext_modules = [
     Extension("a", ["a.pyx"]),
     Extension("b", ["b.pyx"]),
     Extension("c", ["c.pyx"]),
+    Extension("d", ["d.pyx"]),
 ]
 
 ext_modules = cythonize(ext_modules, include_path=["include"])
@@ -37,3 +38,15 @@ ctypedef int my_type
 
 ######## path/c.pxd ########
 +++syntax error just to show that this file is not actually cimported+++
+
+######## path/numpy/__init__.pxd ########
+
+# gh-2905: This should be found before Cython/Inlude/numpy/__init__.pxd
+
+ctypedef int my_type
+
+######## d.pyx ########
+
+cimport numpy
+
+cdef numpy.my_type foo
diff --git a/tests/run/closure_in_derived_class_T2967.pyx b/tests/run/closure_in_derived_class_T2967.pyx
new file mode 100644 (file)
index 0000000..d75c5e5
--- /dev/null
@@ -0,0 +1,18 @@
+# mode: run
+# tag: closures
+# ticket: 2967
+
+cdef class BaseClass:
+    cdef func(self):
+        pass
+cdef class ClosureInsideExtensionClass(BaseClass):
+    """
+    >>> y = ClosureInsideExtensionClass(42)
+    >>> y.test(42)
+    43
+    """
+    cdef func(self):
+        a = 1
+        return (lambda x : x+a)
+    def test(self, b):
+        return self.func()(b)
index 9162498..5e9ab64 100644 (file)
@@ -7,12 +7,19 @@ PYTHON -c "import infile2; import infile3"
 from Cython.Build.Dependencies import cythonize
 from distutils.core import setup
 
-setup(
-    ext_modules = (cythonize("infile*.py") +
-                   cythonize("directive2.py", compiler_directives={'language_level': 2}) +
-                   cythonize("directive3.py", compiler_directives={'language_level': 3})
-                   )
-)
+ext_modules = []
+
+# Test language_level specified in the cythonize() call
+ext_modules += cythonize("directive2.py", compiler_directives={'language_level': 2})
+ext_modules += cythonize("directive3.py", compiler_directives={'language_level': 3})
+
+# Test language_level specified in the source file. We give a
+# conflicting directive to cythonize() to check that the language_level
+# is correctly overridden when compiling
+ext_modules += cythonize("infile2.py", compiler_directives={'language_level': 3})
+ext_modules += cythonize("infile3.py", compiler_directives={'language_level': 2})
+
+setup(ext_modules=ext_modules)
 
 ######## directive3.py ########