diff --git a/docs/markdown/Cython.md b/docs/markdown/Cython.md index 304275043dcc..1c344500b53f 100644 --- a/docs/markdown/Cython.md +++ b/docs/markdown/Cython.md @@ -60,3 +60,23 @@ py.extension_module( dependencies : dep_py, ) ``` + +## Limited API support + +*(New in 1.4)* + +Meson version 1.4 extends the support of the `limited_api` keyword to Cython. + +```meson +project('my project', 'cython') + +py.extension_module( + 'foo', + 'foo.pyx', + dependencies : dep_py, + limited_api : '3.8' +) +``` + +Cython's support for Python's Limited API was introduced in Cython version 3.0. +If the user's Cython version is too old, Meson issues a warning but proceeds. diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 7a2cd2837733..b100d86d8d39 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -173,14 +173,17 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], target_suffix = self.limited_api_suffix limited_api_version_hex = self._convert_api_version_to_py_version_hex(limited_api_version, pydep.version) - limited_api_definition = f'-DPy_LIMITED_API={limited_api_version_hex}' + limited_api_definitions = [ + f'-DPy_LIMITED_API={limited_api_version_hex}', + '-DCYTHON_LIMITED_API=1' + ] new_c_args = mesonlib.extract_as_list(kwargs, 'c_args') - new_c_args.append(limited_api_definition) + new_c_args.extend(limited_api_definitions) kwargs['c_args'] = new_c_args new_cpp_args = mesonlib.extract_as_list(kwargs, 'cpp_args') - new_cpp_args.append(limited_api_definition) + new_cpp_args.extend(limited_api_definitions) kwargs['cpp_args'] = new_cpp_args # When compiled under MSVC, Python's PC/pyconfig.h forcibly inserts pythonMAJOR.MINOR.lib @@ -212,6 +215,10 @@ def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs['link_args'] = new_link_args + cython_compiler = next((c for c in compilers.values() if c.get_id() == 'cython'), None) + if cython_compiler is not None and mesonlib.version_compare(cython_compiler.version, '< 3.0.0'): + mlog.warning(f'Limited API not supported by Cython versions < 3.0.0 (detected: {cython_compiler.version})', location=self.current_node) + kwargs['dependencies'] = new_deps # msys2's python3 has "-cpython-36m.dll", we have to be clever diff --git a/test cases/cython/4 limited api/limited.pyx b/test cases/cython/4 limited api/limited.pyx new file mode 100644 index 000000000000..8207b838a6f9 --- /dev/null +++ b/test cases/cython/4 limited api/limited.pyx @@ -0,0 +1,43 @@ +# This is taken from Cython's limited API tests and +# as a result is under Apache 2.0 + +import cython + +@cython.binding(False) +def fib(int n): + cdef int a, b + a, b = 0, 1 + while b < n: + a, b = b, a + b + return b + +def lsum(values): + cdef long result = 0 + for value in values: + result += value + if type(values) is list: + for value in reversed(values): + result += value + elif type(values) is tuple: + for value in reversed(values): + result += value + return result + +@cython.binding(False) +def raises(): + raise RuntimeError() + +def decode(bytes b, bytearray ba): + return b.decode("utf-8") + ba.decode("utf-8") + +def cast_float(object o): + return float(o) + +class C: + pass + +cdef class D: + pass + +cdef class E(D): + pass diff --git a/test cases/cython/4 limited api/meson.build b/test cases/cython/4 limited api/meson.build new file mode 100644 index 000000000000..d3ed95dd1b8a --- /dev/null +++ b/test cases/cython/4 limited api/meson.build @@ -0,0 +1,19 @@ +project('cython limited api', ['cython', 'c']) + +cythonc = meson.get_compiler('cython') +if cythonc.version() < '3.0' + error('MESON_SKIP_TEST: Cython compiler version does not support limited API') +endif + +py3 = import('python').find_installation() + +ext_mod = py3.extension_module('limited', + 'limited.pyx', + limited_api: '3.7' +) + +test('limited_api runner', + py3, + args : files('run.py'), + env : ['PYTHONPATH=' + meson.current_build_dir()] +) diff --git a/test cases/cython/4 limited api/run.py b/test cases/cython/4 limited api/run.py new file mode 100755 index 000000000000..aa5fb4c1d958 --- /dev/null +++ b/test cases/cython/4 limited api/run.py @@ -0,0 +1,25 @@ +# This is taken from Cython's limited API tests and +# as a result is under Apache 2.0 + +import limited + +limited.fib(11) + +assert limited.lsum(list(range(10))) == 90 +assert limited.lsum(tuple(range(10))) == 90 +assert limited.lsum(iter(range(10))) == 45 + +try: + limited.raises() +except RuntimeError: + pass + +limited.C() +limited.D() +limited.E() + +assert limited.decode(b'a', bytearray(b'b')) == "ab" + +assert limited.cast_float(1) == 1.0 +assert limited.cast_float("2.0") == 2.0 +assert limited.cast_float(bytearray(b"3")) == 3.0