1# gh-91321: Build a basic C++ test extension to check that the Python C API is 2# compatible with C++ and does not emit C++ compiler warnings. 3import os.path 4import sys 5import unittest 6import subprocess 7import sysconfig 8from test import support 9from test.support import os_helper 10 11 12MS_WINDOWS = (sys.platform == 'win32') 13 14 15SETUP_TESTCPPEXT = support.findfile('setup_testcppext.py') 16 17 18@support.requires_subprocess() 19class TestCPPExt(unittest.TestCase): 20 def test_build_cpp11(self): 21 self.check_build(False, '_testcpp11ext') 22 23 def test_build_cpp03(self): 24 self.check_build(True, '_testcpp03ext') 25 26 # With MSVC, the linker fails with: cannot open file 'python311.lib' 27 # https://github.com/python/cpython/pull/32175#issuecomment-1111175897 28 @unittest.skipIf(MS_WINDOWS, 'test fails on Windows') 29 # Building and running an extension in clang sanitizing mode is not 30 # straightforward 31 @unittest.skipIf( 32 '-fsanitize' in (sysconfig.get_config_var('PY_CFLAGS') or ''), 33 'test does not work with analyzing builds') 34 # the test uses venv+pip: skip if it's not available 35 @support.requires_venv_with_pip() 36 def check_build(self, std_cpp03, extension_name): 37 # Build in a temporary directory 38 with os_helper.temp_cwd(): 39 self._check_build(std_cpp03, extension_name) 40 41 def _check_build(self, std_cpp03, extension_name): 42 venv_dir = 'env' 43 verbose = support.verbose 44 45 # Create virtual environment to get setuptools 46 cmd = [sys.executable, '-X', 'dev', '-m', 'venv', venv_dir] 47 if verbose: 48 print() 49 print('Run:', ' '.join(cmd)) 50 subprocess.run(cmd, check=True) 51 52 # Get the Python executable of the venv 53 python_exe = 'python' 54 if sys.executable.endswith('.exe'): 55 python_exe += '.exe' 56 if MS_WINDOWS: 57 python = os.path.join(venv_dir, 'Scripts', python_exe) 58 else: 59 python = os.path.join(venv_dir, 'bin', python_exe) 60 61 def run_cmd(operation, cmd): 62 if verbose: 63 print('Run:', ' '.join(cmd)) 64 subprocess.run(cmd, check=True) 65 else: 66 proc = subprocess.run(cmd, 67 stdout=subprocess.PIPE, 68 stderr=subprocess.STDOUT, 69 text=True) 70 if proc.returncode: 71 print(proc.stdout, end='') 72 self.fail( 73 f"{operation} failed with exit code {proc.returncode}") 74 75 # Build the C++ extension 76 cmd = [python, '-X', 'dev', 77 SETUP_TESTCPPEXT, 'build_ext', '--verbose'] 78 if std_cpp03: 79 cmd.append('-std=c++03') 80 run_cmd('Build', cmd) 81 82 # Install the C++ extension 83 cmd = [python, '-X', 'dev', 84 SETUP_TESTCPPEXT, 'install'] 85 run_cmd('Install', cmd) 86 87 # Do a reference run. Until we test that running python 88 # doesn't leak references (gh-94755), run it so one can manually check 89 # -X showrefcount results against this baseline. 90 cmd = [python, 91 '-X', 'dev', 92 '-X', 'showrefcount', 93 '-c', 'pass'] 94 run_cmd('Reference run', cmd) 95 96 # Import the C++ extension 97 cmd = [python, 98 '-X', 'dev', 99 '-X', 'showrefcount', 100 '-c', f"import {extension_name}"] 101 run_cmd('Import', cmd) 102 103 104if __name__ == "__main__": 105 unittest.main() 106