| 
									
										
										
										
											2022-05-02 14:09:22 +02:00
										 |  |  | # 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. | 
					
						
							| 
									
										
										
										
											2022-05-13 00:20:13 +02:00
										 |  |  | import os.path | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  | import shlex | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2022-05-13 00:20:13 +02:00
										 |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2022-05-02 14:09:22 +02:00
										 |  |  | from test import support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-22 20:30:18 +02:00
										 |  |  | SOURCE = os.path.join(os.path.dirname(__file__), 'extension.cpp') | 
					
						
							|  |  |  | SETUP = os.path.join(os.path.dirname(__file__), 'setup.py') | 
					
						
							| 
									
										
										
										
											2022-05-02 14:09:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  | # With MSVC on a debug build, the linker fails with: cannot open file | 
					
						
							|  |  |  | # 'python311.lib', it should look 'python311_d.lib'. | 
					
						
							|  |  |  | @unittest.skipIf(support.MS_WINDOWS and support.Py_DEBUG, | 
					
						
							|  |  |  |                  'test fails on Windows debug build') | 
					
						
							| 
									
										
										
										
											2024-03-19 00:14:29 +01:00
										 |  |  | # Building and running an extension in clang sanitizing mode is not | 
					
						
							|  |  |  | # straightforward | 
					
						
							|  |  |  | @support.skip_if_sanitizer('test does not work with analyzing builds', | 
					
						
							|  |  |  |                            address=True, memory=True, ub=True, thread=True) | 
					
						
							|  |  |  | # the test uses venv+pip: skip if it's not available | 
					
						
							|  |  |  | @support.requires_venv_with_pip() | 
					
						
							| 
									
										
										
										
											2022-05-06 00:08:43 +02:00
										 |  |  | @support.requires_subprocess() | 
					
						
							| 
									
										
										
										
											2024-03-19 00:14:29 +01:00
										 |  |  | @support.requires_resource('cpu') | 
					
						
							| 
									
										
										
										
											2022-05-02 14:09:22 +02:00
										 |  |  | class TestCPPExt(unittest.TestCase): | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |     def test_build(self): | 
					
						
							|  |  |  |         self.check_build('_testcppext') | 
					
						
							| 
									
										
										
										
											2022-06-14 11:43:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_build_cpp03(self): | 
					
						
							| 
									
										
										
										
											2025-03-04 14:10:09 +01:00
										 |  |  |         # In public docs, we say C API is compatible with C++11. However, | 
					
						
							|  |  |  |         # in practice we do maintain C++03 compatibility in public headers. | 
					
						
							|  |  |  |         # Please ask the C API WG before adding a new C++11-only feature. | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |         self.check_build('_testcpp03ext', std='c++03') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-04 14:10:09 +01:00
										 |  |  |     @support.requires_gil_enabled('incompatible with Free Threading') | 
					
						
							|  |  |  |     def test_build_limited_cpp03(self): | 
					
						
							|  |  |  |         self.check_build('_test_limited_cpp03ext', std='c++03', limited=True) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |     @unittest.skipIf(support.MS_WINDOWS, "MSVC doesn't support /std:c++11") | 
					
						
							|  |  |  |     def test_build_cpp11(self): | 
					
						
							|  |  |  |         self.check_build('_testcpp11ext', std='c++11') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-19 22:58:13 +01:00
										 |  |  |     # Only test C++14 on MSVC. | 
					
						
							|  |  |  |     # On s390x RHEL7, GCC 4.8.5 doesn't support C++14. | 
					
						
							|  |  |  |     @unittest.skipIf(not support.MS_WINDOWS, "need Windows") | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |     def test_build_cpp14(self): | 
					
						
							|  |  |  |         self.check_build('_testcpp14ext', std='c++14') | 
					
						
							| 
									
										
										
										
											2022-06-14 11:43:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-13 14:23:20 +01:00
										 |  |  |     @support.requires_gil_enabled('incompatible with Free Threading') | 
					
						
							|  |  |  |     def test_build_limited(self): | 
					
						
							|  |  |  |         self.check_build('_testcppext_limited', limited=True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_build(self, extension_name, std=None, limited=False): | 
					
						
							| 
									
										
										
										
											2023-05-26 15:32:03 +02:00
										 |  |  |         venv_dir = 'env' | 
					
						
							| 
									
										
										
										
											2025-04-25 00:46:20 +01:00
										 |  |  |         with support.setup_venv_with_pip_setuptools(venv_dir) as python_exe: | 
					
						
							| 
									
										
										
										
											2024-12-13 14:23:20 +01:00
										 |  |  |             self._check_build(extension_name, python_exe, | 
					
						
							|  |  |  |                               std=std, limited=limited) | 
					
						
							| 
									
										
										
										
											2022-05-13 00:20:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-13 14:23:20 +01:00
										 |  |  |     def _check_build(self, extension_name, python_exe, std, limited): | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |         pkg_dir = 'pkg' | 
					
						
							|  |  |  |         os.mkdir(pkg_dir) | 
					
						
							| 
									
										
										
										
											2023-08-22 20:30:18 +02:00
										 |  |  |         shutil.copy(SETUP, os.path.join(pkg_dir, os.path.basename(SETUP))) | 
					
						
							|  |  |  |         shutil.copy(SOURCE, os.path.join(pkg_dir, os.path.basename(SOURCE))) | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |         def run_cmd(operation, cmd): | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |             env = os.environ.copy() | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |             if std: | 
					
						
							|  |  |  |                 env['CPYTHON_TEST_CPP_STD'] = std | 
					
						
							| 
									
										
										
										
											2024-12-13 14:23:20 +01:00
										 |  |  |             if limited: | 
					
						
							|  |  |  |                 env['CPYTHON_TEST_LIMITED'] = '1' | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |             env['CPYTHON_TEST_EXT_NAME'] = extension_name | 
					
						
							| 
									
										
										
										
											2023-05-26 15:32:03 +02:00
										 |  |  |             if support.verbose: | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |                 print('Run:', ' '.join(map(shlex.quote, cmd))) | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |                 subprocess.run(cmd, check=True, env=env) | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 proc = subprocess.run(cmd, | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |                                       env=env, | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |                                       stdout=subprocess.PIPE, | 
					
						
							|  |  |  |                                       stderr=subprocess.STDOUT, | 
					
						
							|  |  |  |                                       text=True) | 
					
						
							|  |  |  |                 if proc.returncode: | 
					
						
							| 
									
										
										
										
											2024-03-19 15:03:27 +01:00
										 |  |  |                     print('Run:', ' '.join(map(shlex.quote, cmd))) | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |                     print(proc.stdout, end='') | 
					
						
							|  |  |  |                     self.fail( | 
					
						
							|  |  |  |                         f"{operation} failed with exit code {proc.returncode}") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |         # Build and install the C++ extension | 
					
						
							| 
									
										
										
										
											2023-05-26 15:32:03 +02:00
										 |  |  |         cmd = [python_exe, '-X', 'dev', | 
					
						
							| 
									
										
										
										
											2023-04-12 23:17:36 -05:00
										 |  |  |                '-m', 'pip', 'install', '--no-build-isolation', | 
					
						
							|  |  |  |                os.path.abspath(pkg_dir)] | 
					
						
							| 
									
										
										
										
											2024-06-28 14:41:37 +02:00
										 |  |  |         if support.verbose: | 
					
						
							|  |  |  |             cmd.append('-v') | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |         run_cmd('Install', cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Do a reference run. Until we test that running python | 
					
						
							|  |  |  |         # doesn't leak references (gh-94755), run it so one can manually check | 
					
						
							|  |  |  |         # -X showrefcount results against this baseline. | 
					
						
							| 
									
										
										
										
											2023-05-26 15:32:03 +02:00
										 |  |  |         cmd = [python_exe, | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |                '-X', 'dev', | 
					
						
							|  |  |  |                '-X', 'showrefcount', | 
					
						
							|  |  |  |                '-c', 'pass'] | 
					
						
							|  |  |  |         run_cmd('Reference run', cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Import the C++ extension | 
					
						
							| 
									
										
										
										
											2023-05-26 15:32:03 +02:00
										 |  |  |         cmd = [python_exe, | 
					
						
							| 
									
										
										
										
											2022-07-12 17:06:05 +02:00
										 |  |  |                '-X', 'dev', | 
					
						
							|  |  |  |                '-X', 'showrefcount', | 
					
						
							|  |  |  |                '-c', f"import {extension_name}"] | 
					
						
							|  |  |  |         run_cmd('Import', cmd) | 
					
						
							| 
									
										
										
										
											2022-05-02 14:09:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     unittest.main() |