gh-91321: Add _testcppext C++ extension (#32175)

Build a basic C++ test extension to check that the Python C API is
compatible with C++ and does not emit C++ compiler warnings.

* Add Modules/_testcppext.cpp: C++ extension
* Add Lib/test/test_cppext.py: test building the C++ extension.
This commit is contained in:
Victor Stinner 2022-05-02 14:09:22 +02:00 committed by GitHub
parent 18b07d773e
commit 79886e7b62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 141 additions and 0 deletions

62
Lib/test/_testcppext.cpp Normal file
View file

@ -0,0 +1,62 @@
// gh-91321: Very basic C++ test extension to check that the Python C API is
// compatible with C++ and does not emit C++ compiler warnings.
#include "Python.h"
PyDoc_STRVAR(_testcppext_add_doc,
"add(x, y)\n"
"\n"
"Return the sum of two integers: x + y.");
static PyObject *
_testcppext_add(PyObject *Py_UNUSED(module), PyObject *args)
{
long i, j;
if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) {
return nullptr;
}
long res = i + j;
return PyLong_FromLong(res);
}
static PyMethodDef _testcppext_methods[] = {
{"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc},
{nullptr, nullptr, 0, nullptr} /* sentinel */
};
static int
_testcppext_exec(PyObject *module)
{
if (PyModule_AddIntMacro(module, __cplusplus) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot _testcppext_slots[] = {
{Py_mod_exec, reinterpret_cast<void*>(_testcppext_exec)},
{0, NULL}
};
PyDoc_STRVAR(_testcppext_doc, "C++ test extension.");
static struct PyModuleDef _testcppext_module = {
PyModuleDef_HEAD_INIT, // m_base
"_testcppext", // m_name
_testcppext_doc, // m_doc
0, // m_size
_testcppext_methods, // m_methods
_testcppext_slots, // m_slots
NULL, // m_traverse
NULL, // m_clear
nullptr, // m_free
};
PyMODINIT_FUNC
PyInit__testcppext(void)
{
return PyModuleDef_Init(&_testcppext_module);
}

79
Lib/test/test_cppext.py Normal file
View file

@ -0,0 +1,79 @@
# 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 sys
import unittest
import warnings
from test import support
from test.support import os_helper
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
from distutils.core import setup, Extension
import distutils.sysconfig
MS_WINDOWS = (sys.platform == 'win32')
SOURCE = support.findfile('_testcppext.cpp')
if not MS_WINDOWS:
# C++ compiler flags for GCC and clang
CPPFLAGS = [
# Python currently targets C++11
'-std=c++11',
# gh-91321: The purpose of _testcppext 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
CPPFLAGS = []
class TestCPPExt(unittest.TestCase):
def build(self):
cpp_ext = Extension(
'_testcppext',
sources=[SOURCE],
language='c++',
extra_compile_args=CPPFLAGS)
try:
try:
with (support.captured_stdout() as stdout,
support.swap_attr(sys, 'argv', ['setup.py', 'build_ext'])):
setup(name="_testcppext", ext_modules=[cpp_ext])
return
except:
# Show output on error
print()
print(stdout.getvalue())
raise
except SystemExit:
self.fail("Build failed")
# With MSVC, the linker fails with: cannot open file 'python311.lib'
# https://github.com/python/cpython/pull/32175#issuecomment-1111175897
@unittest.skipIf(MS_WINDOWS, 'test fails on Windows')
def test_build(self):
# save/restore os.environ
def restore_env(old_env):
os.environ.clear()
os.environ.update(old_env)
self.addCleanup(restore_env, dict(os.environ))
def restore_sysconfig_vars(old_config_vars):
distutils.sysconfig._config_vars.clear()
distutils.sysconfig._config_vars.update(old_config_vars)
self.addCleanup(restore_sysconfig_vars,
dict(distutils.sysconfig._config_vars))
# Build in a temporary directory
with os_helper.temp_cwd():
self.build()
if __name__ == "__main__":
unittest.main()