gh-92584: test_cppext uses setuptools (#92639)

Rewrite test_cppext to run in a virtual environment and to build the
C++ extension with setuptools rather than distutils.
This commit is contained in:
Victor Stinner 2022-05-13 00:20:13 +02:00 committed by GitHub
parent 8cf2906828
commit 4e28377722
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 67 deletions

View file

@ -0,0 +1,42 @@
# 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 sys
from test import support
from setuptools import setup, Extension
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',
# Warn on old-style cast (C cast) like: (PyObject*)op
'-Wold-style-cast',
# Warn when using NULL rather than _Py_NULL in static inline functions
'-Wzero-as-null-pointer-constant',
]
else:
# Don't pass any compiler flag to MSVC
CPPFLAGS = []
def main():
cpp_ext = Extension(
'_testcppext',
sources=[SOURCE],
language='c++',
extra_compile_args=CPPFLAGS)
setup(name="_testcppext", ext_modules=[cpp_ext])
if __name__ == "__main__":
main()

View file

@ -1,91 +1,59 @@
# gh-91321: Build a basic C++ test extension to check that the Python C API is # 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. # compatible with C++ and does not emit C++ compiler warnings.
import contextlib import os.path
import os
import sys import sys
import unittest import unittest
import warnings import subprocess
from test import support from test import support
from test.support import os_helper 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') MS_WINDOWS = (sys.platform == 'win32')
SOURCE = support.findfile('_testcppext.cpp') SETUP_TESTCPPEXT = support.findfile('setup_testcppext.py')
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',
# Warn on old-style cast (C cast) like: (PyObject*)op
'-Wold-style-cast',
# Warn when using NULL rather than _Py_NULL in static inline functions
'-Wzero-as-null-pointer-constant',
]
else:
# Don't pass any compiler flag to MSVC
CPPFLAGS = []
@support.requires_subprocess() @support.requires_subprocess()
class TestCPPExt(unittest.TestCase): class TestCPPExt(unittest.TestCase):
def build(self):
cpp_ext = Extension(
'_testcppext',
sources=[SOURCE],
language='c++',
extra_compile_args=CPPFLAGS)
capture_stdout = (not support.verbose)
try:
try:
if capture_stdout:
stdout = support.captured_stdout()
else:
print()
stdout = contextlib.nullcontext()
with (stdout,
support.swap_attr(sys, 'argv', ['setup.py', 'build_ext', '--verbose'])):
setup(name="_testcppext", ext_modules=[cpp_ext])
return
except:
if capture_stdout:
# 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' # With MSVC, the linker fails with: cannot open file 'python311.lib'
# https://github.com/python/cpython/pull/32175#issuecomment-1111175897 # https://github.com/python/cpython/pull/32175#issuecomment-1111175897
@unittest.skipIf(MS_WINDOWS, 'test fails on Windows') @unittest.skipIf(MS_WINDOWS, 'test fails on Windows')
def test_build(self): 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 # Build in a temporary directory
with os_helper.temp_cwd(): with os_helper.temp_cwd():
self.build() self._test_build()
def _test_build(self):
venv_dir = 'env'
# Create virtual environment to get setuptools
cmd = [sys.executable, '-X', 'dev', '-m', 'venv', venv_dir]
if support.verbose:
print()
print('Run:', ' '.join(cmd))
subprocess.run(cmd, check=True)
# Get the Python executable of the venv
python_exe = 'python'
if sys.executable.endswith('.exe'):
python_exe += '.exe'
if MS_WINDOWS:
python = os.path.join(venv_dir, 'Scripts', python_exe)
else:
python = os.path.join(venv_dir, 'bin', python_exe)
# Build the C++ extension
cmd = [python, '-X', 'dev', SETUP_TESTCPPEXT, 'build_ext', '--verbose']
if support.verbose:
print('Run:', ' '.join(cmd))
proc = subprocess.run(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True)
if proc.returncode:
print(proc.stdout, end='')
self.fail(f"Build failed with exit code {proc.returncode}")
if __name__ == "__main__": if __name__ == "__main__":