Skip to content

Commit

Permalink
gh-91321: Add _testcppext C++ extension (#32175)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vstinner authored May 2, 2022
1 parent 18b07d7 commit 79886e7
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
62 changes: 62 additions & 0 deletions Lib/test/_testcppext.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 79 additions & 0 deletions Lib/test/test_cppext.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 79886e7

Please sign in to comment.