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)
====================
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,
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'])
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
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.
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)
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__']
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)
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
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,
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
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:
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:
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)
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)
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):
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:
# cython.* namespace for pure mode.
from __future__ import absolute_import
-__version__ = "0.29.10"
+__version__ = "0.29.11"
try:
from __builtin__ import basestring
#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)
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();
@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:
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'])
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"])
######## 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
--- /dev/null
+# 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)
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 ########