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.
3 import os.path
4 import shutil
5 import sys
6 import unittest
7 import subprocess
8 import sysconfig
9 from test import support
10 from test.support import os_helper
11
12
13 MS_WINDOWS = (sys.platform == 'win32')
14 SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp')
15 SETUP = os.path.join(os.path.dirname(__file__), 'setup.py')
16
17
18 @support.requires_subprocess()
19 class ESC[4;38;5;81mTestCPPExt(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
20 @support.requires_resource('cpu')
21 def test_build_cpp11(self):
22 self.check_build(False, '_testcpp11ext')
23
24 @support.requires_resource('cpu')
25 def test_build_cpp03(self):
26 self.check_build(True, '_testcpp03ext')
27
28 # With MSVC, the linker fails with: cannot open file 'python311.lib'
29 # https://github.com/python/cpython/pull/32175#issuecomment-1111175897
30 @unittest.skipIf(MS_WINDOWS, 'test fails on Windows')
31 # Building and running an extension in clang sanitizing mode is not
32 # straightforward
33 @unittest.skipIf(
34 '-fsanitize' in (sysconfig.get_config_var('PY_CFLAGS') or ''),
35 'test does not work with analyzing builds')
36 # the test uses venv+pip: skip if it's not available
37 @support.requires_venv_with_pip()
38 def check_build(self, std_cpp03, extension_name):
39 venv_dir = 'env'
40 with support.setup_venv_with_pip_setuptools_wheel(venv_dir) as python_exe:
41 self._check_build(std_cpp03, extension_name, python_exe)
42
43 def _check_build(self, std_cpp03, extension_name, python_exe):
44 pkg_dir = 'pkg'
45 os.mkdir(pkg_dir)
46 shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP)))
47 shutil.copy(SOURCE, os.path.join(pkg_dir, os.path.basename(SOURCE)))
48
49 def run_cmd(operation, cmd):
50 env = os.environ.copy()
51 env['CPYTHON_TEST_CPP_STD'] = 'c++03' if std_cpp03 else 'c++11'
52 env['CPYTHON_TEST_EXT_NAME'] = extension_name
53 if support.verbose:
54 print('Run:', ' '.join(cmd))
55 subprocess.run(cmd, check=True, env=env)
56 else:
57 proc = subprocess.run(cmd,
58 env=env,
59 stdout=subprocess.PIPE,
60 stderr=subprocess.STDOUT,
61 text=True)
62 if proc.returncode:
63 print(proc.stdout, end='')
64 self.fail(
65 f"{operation} failed with exit code {proc.returncode}")
66
67 # Build and install the C++ extension
68 cmd = [python_exe, '-X', 'dev',
69 '-m', 'pip', 'install', '--no-build-isolation',
70 os.path.abspath(pkg_dir)]
71 run_cmd('Install', cmd)
72
73 # Do a reference run. Until we test that running python
74 # doesn't leak references (gh-94755), run it so one can manually check
75 # -X showrefcount results against this baseline.
76 cmd = [python_exe,
77 '-X', 'dev',
78 '-X', 'showrefcount',
79 '-c', 'pass']
80 run_cmd('Reference run', cmd)
81
82 # Import the C++ extension
83 cmd = [python_exe,
84 '-X', 'dev',
85 '-X', 'showrefcount',
86 '-c', f"import {extension_name}"]
87 run_cmd('Import', cmd)
88
89
90 if __name__ == "__main__":
91 unittest.main()