https://github.com/python/cpython/commit/590a26010d5d7f27890f89820645580bb8f28547
commit: 590a26010d5d7f27890f89820645580bb8f28547
branch: main
author: Victor Stinner <vstin...@python.org>
committer: vstinner <vstin...@python.org>
date: 2024-03-18T20:15:20+01:00
summary:

gh-116869: Add test_cext test: build a C extension (#116954)

files:
A Lib/test/test_cext/__init__.py
A Lib/test/test_cext/extension.c
A Lib/test/test_cext/setup.py
A Misc/NEWS.d/next/C API/2024-03-18-10-58-47.gh-issue-116869.lN0GBl.rst
M Makefile.pre.in

diff --git a/Lib/test/test_cext/__init__.py b/Lib/test/test_cext/__init__.py
new file mode 100644
index 00000000000000..302ea3d3a82300
--- /dev/null
+++ b/Lib/test/test_cext/__init__.py
@@ -0,0 +1,92 @@
+# gh-116869: Build a basic C test extension to check that the Python C API
+# does not emit C compiler warnings.
+
+import os.path
+import shutil
+import subprocess
+import sysconfig
+import unittest
+from test import support
+
+
+SOURCE = os.path.join(os.path.dirname(__file__), 'extension.c')
+SETUP = os.path.join(os.path.dirname(__file__), 'setup.py')
+
+
+# gh-110119: pip does not currently support 't' in the ABI flag use by
+# --disable-gil builds. Once it does, we can remove this skip.
+@unittest.skipIf(support.Py_GIL_DISABLED,
+                 'test does not work with --disable-gil')
+@support.requires_subprocess()
+@support.requires_resource('cpu')
+class TestExt(unittest.TestCase):
+    def test_build_c99(self):
+        self.check_build('c99', '_test_c99_ext')
+
+    def test_build_c11(self):
+        self.check_build('c11', '_test_c11_ext')
+
+    # With MSVC, the linker fails with: cannot open file 'python311.lib'
+    # https://github.com/python/cpython/pull/32175#issuecomment-1111175897
+    @unittest.skipIf(support.MS_WINDOWS, 'test fails on Windows')
+    # Building and running an extension in clang sanitizing mode is not
+    # straightforward
+    @unittest.skipIf(
+        '-fsanitize' in (sysconfig.get_config_var('PY_CFLAGS') or ''),
+        'test does not work with analyzing builds')
+    # the test uses venv+pip: skip if it's not available
+    @support.requires_venv_with_pip()
+    def check_build(self, clang_std, extension_name):
+        venv_dir = 'env'
+        with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as 
python_exe:
+            self._check_build(clang_std, extension_name, python_exe)
+
+    def _check_build(self, clang_std, extension_name, python_exe):
+        pkg_dir = 'pkg'
+        os.mkdir(pkg_dir)
+        shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP)))
+        shutil.copy(SOURCE, os.path.join(pkg_dir, os.path.basename(SOURCE)))
+
+        def run_cmd(operation, cmd):
+            env = os.environ.copy()
+            env['CPYTHON_TEST_STD'] = clang_std
+            env['CPYTHON_TEST_EXT_NAME'] = extension_name
+            if support.verbose:
+                print('Run:', ' '.join(cmd))
+                subprocess.run(cmd, check=True, env=env)
+            else:
+                proc = subprocess.run(cmd,
+                                      env=env,
+                                      stdout=subprocess.PIPE,
+                                      stderr=subprocess.STDOUT,
+                                      text=True)
+                if proc.returncode:
+                    print(proc.stdout, end='')
+                    self.fail(
+                        f"{operation} failed with exit code {proc.returncode}")
+
+        # Build and install the C extension
+        cmd = [python_exe, '-X', 'dev',
+               '-m', 'pip', 'install', '--no-build-isolation',
+               os.path.abspath(pkg_dir)]
+        run_cmd('Install', cmd)
+
+        # Do a reference run. Until we test that running python
+        # doesn't leak references (gh-94755), run it so one can manually check
+        # -X showrefcount results against this baseline.
+        cmd = [python_exe,
+               '-X', 'dev',
+               '-X', 'showrefcount',
+               '-c', 'pass']
+        run_cmd('Reference run', cmd)
+
+        # Import the C extension
+        cmd = [python_exe,
+               '-X', 'dev',
+               '-X', 'showrefcount',
+               '-c', f"import {extension_name}"]
+        run_cmd('Import', cmd)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c
new file mode 100644
index 00000000000000..cfecad39f8af14
--- /dev/null
+++ b/Lib/test/test_cext/extension.c
@@ -0,0 +1,80 @@
+// gh-116869: Basic C test extension to check that the Python C API
+// does not emit C compiler warnings.
+
+// Always enable assertions
+#undef NDEBUG
+
+#include "Python.h"
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L
+#  define NAME _test_c2x_ext
+#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#  define NAME _test_c11_ext
+#else
+#  define NAME _test_c99_ext
+#endif
+
+#define _STR(NAME) #NAME
+#define STR(NAME) _STR(NAME)
+
+PyDoc_STRVAR(_testcext_add_doc,
+"add(x, y)\n"
+"\n"
+"Return the sum of two integers: x + y.");
+
+static PyObject *
+_testcext_add(PyObject *Py_UNUSED(module), PyObject *args)
+{
+    long i, j;
+    if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) {
+        return NULL;
+    }
+    long res = i + j;
+    return PyLong_FromLong(res);
+}
+
+
+static PyMethodDef _testcext_methods[] = {
+    {"add", _testcext_add, METH_VARARGS, _testcext_add_doc},
+    {NULL, NULL, 0, NULL}  // sentinel
+};
+
+
+static int
+_testcext_exec(PyObject *module)
+{
+    if (PyModule_AddIntMacro(module, __STDC_VERSION__) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static PyModuleDef_Slot _testcext_slots[] = {
+    {Py_mod_exec, _testcext_exec},
+    {0, NULL}
+};
+
+
+PyDoc_STRVAR(_testcext_doc, "C test extension.");
+
+static struct PyModuleDef _testcext_module = {
+    PyModuleDef_HEAD_INIT,  // m_base
+    STR(NAME),  // m_name
+    _testcext_doc,  // m_doc
+    0,  // m_size
+    _testcext_methods,  // m_methods
+    _testcext_slots,  // m_slots
+    NULL,  // m_traverse
+    NULL,  // m_clear
+    NULL,  // m_free
+};
+
+
+#define _FUNC_NAME(NAME) PyInit_ ## NAME
+#define FUNC_NAME(NAME) _FUNC_NAME(NAME)
+
+PyMODINIT_FUNC
+FUNC_NAME(NAME)(void)
+{
+    return PyModuleDef_Init(&_testcext_module);
+}
diff --git a/Lib/test/test_cext/setup.py b/Lib/test/test_cext/setup.py
new file mode 100644
index 00000000000000..dd57a5fb8e1e1b
--- /dev/null
+++ b/Lib/test/test_cext/setup.py
@@ -0,0 +1,47 @@
+# gh-91321: Build a basic C test extension to check that the Python C API is
+# compatible with C and does not emit C compiler warnings.
+import os
+import shlex
+import sys
+import sysconfig
+from test import support
+
+from setuptools import setup, Extension
+
+
+SOURCE = 'extension.c'
+if not support.MS_WINDOWS:
+    # C compiler flags for GCC and clang
+    CFLAGS = [
+        # The purpose of test_cext extension is to check that building a C
+        # extension using the Python C API does not emit C compiler warnings.
+        '-Werror',
+    ]
+else:
+    # Don't pass any compiler flag to MSVC
+    CFLAGS = []
+
+
+def main():
+    std = os.environ["CPYTHON_TEST_STD"]
+    name = os.environ["CPYTHON_TEST_EXT_NAME"]
+    cflags = [*CFLAGS, f'-std={std}']
+
+    # Remove existing -std options to only test ours
+    cmd = (sysconfig.get_config_var('CC') or '')
+    if cmd is not None:
+        cmd = shlex.split(cmd)
+        cmd = [arg for arg in cmd if not arg.startswith('-std=')]
+        cmd = shlex.join(cmd)
+        # CC env var overrides sysconfig CC variable in setuptools
+        os.environ['CC'] = cmd
+
+    ext = Extension(
+        name,
+        sources=[SOURCE],
+        extra_compile_args=cflags)
+    setup(name='internal' + name, version='0.0', ext_modules=[ext])
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 5958b6bdb05a57..404e7ee3e42054 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2330,6 +2330,7 @@ TESTSUBDIRS=      idlelib/idle_test \
                test/support/interpreters \
                test/test_asyncio \
                test/test_capi \
+               test/test_cext \
                test/test_concurrent_futures \
                test/test_cppext \
                test/test_ctypes \
diff --git a/Misc/NEWS.d/next/C 
API/2024-03-18-10-58-47.gh-issue-116869.lN0GBl.rst b/Misc/NEWS.d/next/C 
API/2024-03-18-10-58-47.gh-issue-116869.lN0GBl.rst
new file mode 100644
index 00000000000000..71044b4930355a
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-03-18-10-58-47.gh-issue-116869.lN0GBl.rst     
@@ -0,0 +1,2 @@
+Add ``test_cext`` test: build a C extension to check if the Python C API
+emits C compiler warnings. Patch by Victor Stinner.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to