Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitef591cf

Browse files
authored
gh-91321: Fix compatibility with C++ older than C++11 (#93784) (#93802)
* Fix the compatibility of the Python C API with C++ older than C++11.* _Py_NULL is only defined as nullptr on C++11 and newer.(cherry picked from commit4caf5c2)* test_cppext now builds the C++ extension with setuptools.* Add @test.support.requires_venv_with_pip.(cherry picked from commitca0cc9c)
1 parent871b1dc commitef591cf

File tree

7 files changed

+156
-88
lines changed

7 files changed

+156
-88
lines changed

‎Include/pyport.h‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ extern "C++" {
3636
inline type_Py_CAST_impl(int ptr) {
3737
returnreinterpret_cast<type>(ptr);
3838
}
39+
#if __cplusplus >= 201103
3940
template<typename type>
4041
inline type_Py_CAST_impl(std::nullptr_t) {
4142
returnstatic_cast<type>(nullptr);
4243
}
44+
#endif
4345

4446
template<typename type,typename expr_type>
4547
inline type_Py_CAST_impl(expr_type *expr) {
@@ -70,8 +72,9 @@ extern "C++" {
7072
#endif
7173

7274
// Static inline functions should use _Py_NULL rather than using directly NULL
73-
// to prevent C++ compiler warnings. In C++, _Py_NULL uses nullptr.
74-
#ifdef __cplusplus
75+
// to prevent C++ compiler warnings. On C++11 and newer, _Py_NULL is defined as
76+
// nullptr.
77+
#if defined(__cplusplus) && __cplusplus >= 201103
7578
#define_Py_NULLnullptr
7679
#else
7780
#define_Py_NULLNULL

‎Lib/test/_testcppext.cpp‎

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
#include"Python.h"
88

9+
#if __cplusplus >= 201103
10+
#defineNAME _testcpp11ext
11+
#else
12+
#defineNAME _testcpp03ext
13+
#endif
14+
915
PyDoc_STRVAR(_testcppext_add_doc,
1016
"add(x, y)\n"
1117
"\n"
@@ -16,7 +22,7 @@ _testcppext_add(PyObject *Py_UNUSED(module), PyObject *args)
1622
{
1723
long i, j;
1824
if (!PyArg_ParseTuple(args,"ll:foo", &i, &j)) {
19-
returnnullptr;
25+
return_Py_NULL;
2026
}
2127
long res = i + j;
2228
returnPyLong_FromLong(res);
@@ -47,8 +53,8 @@ static PyObject *
4753
test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
4854
{
4955
PyObject *obj =Py_BuildValue("(ii)",1,2);
50-
if (obj ==nullptr) {
51-
returnnullptr;
56+
if (obj ==_Py_NULL) {
57+
return_Py_NULL;
5258
}
5359
Py_ssize_t refcnt =Py_REFCNT(obj);
5460
assert(refcnt >=1);
@@ -77,9 +83,11 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
7783
// gh-93442: Pass 0 as NULL for PyObject*
7884
Py_XINCREF(0);
7985
Py_XDECREF(0);
80-
// ensure that nullptr works too
86+
#if _cplusplus >= 201103
87+
// Test nullptr passed as PyObject*
8188
Py_XINCREF(nullptr);
8289
Py_XDECREF(nullptr);
90+
#endif
8391

8492
Py_DECREF(obj);
8593
Py_RETURN_NONE;
@@ -90,16 +98,16 @@ static PyObject *
9098
test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
9199
{
92100
PyObject *str =PyUnicode_FromString("abc");
93-
if (str ==nullptr) {
94-
returnnullptr;
101+
if (str ==_Py_NULL) {
102+
return_Py_NULL;
95103
}
96104

97105
assert(PyUnicode_Check(str));
98106
assert(PyUnicode_GET_LENGTH(str) ==3);
99107

100108
// gh-92800: test PyUnicode_READ()
101109
constvoid* data =PyUnicode_DATA(str);
102-
assert(data !=nullptr);
110+
assert(data !=_Py_NULL);
103111
int kind =PyUnicode_KIND(str);
104112
assert(kind == PyUnicode_1BYTE_KIND);
105113
assert(PyUnicode_READ(kind, data,0) =='a');
@@ -118,9 +126,9 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
118126

119127
static PyMethodDef _testcppext_methods[] = {
120128
{"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc},
121-
{"test_api_casts", test_api_casts, METH_NOARGS,nullptr},
122-
{"test_unicode", test_unicode, METH_NOARGS,nullptr},
123-
{nullptr,nullptr,0,nullptr}/* sentinel*/
129+
{"test_api_casts", test_api_casts, METH_NOARGS,_Py_NULL},
130+
{"test_unicode", test_unicode, METH_NOARGS,_Py_NULL},
131+
{_Py_NULL, _Py_NULL,0,_Py_NULL}/* sentinel*/
124132
};
125133

126134

@@ -135,26 +143,32 @@ _testcppext_exec(PyObject *module)
135143

136144
static PyModuleDef_Slot _testcppext_slots[] = {
137145
{Py_mod_exec,reinterpret_cast<void*>(_testcppext_exec)},
138-
{0,nullptr}
146+
{0,_Py_NULL}
139147
};
140148

141149

142150
PyDoc_STRVAR(_testcppext_doc,"C++ test extension.");
143151

152+
#define_STR(NAME) #NAME
153+
#defineSTR(NAME) _STR(NAME)
154+
144155
staticstructPyModuleDef _testcppext_module = {
145156
PyModuleDef_HEAD_INIT,// m_base
146-
"_testcppext",// m_name
157+
STR(NAME),// m_name
147158
_testcppext_doc,// m_doc
148159
0,// m_size
149160
_testcppext_methods,// m_methods
150161
_testcppext_slots,// m_slots
151-
nullptr,// m_traverse
152-
nullptr,// m_clear
153-
nullptr,// m_free
162+
_Py_NULL,// m_traverse
163+
_Py_NULL,// m_clear
164+
_Py_NULL,// m_free
154165
};
155166

167+
#define_FUNC_NAME(NAME) PyInit_ ## NAME
168+
#defineFUNC_NAME(NAME) _FUNC_NAME(NAME)
169+
156170
PyMODINIT_FUNC
157-
PyInit__testcppext(void)
171+
FUNC_NAME(NAME)(void)
158172
{
159173
returnPyModuleDef_Init(&_testcppext_module);
160174
}

‎Lib/test/setup_testcppext.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
importsys
4+
fromtestimportsupport
5+
6+
fromsetuptoolsimportsetup,Extension
7+
8+
9+
MS_WINDOWS= (sys.platform=='win32')
10+
11+
12+
SOURCE=support.findfile('_testcppext.cpp')
13+
ifnotMS_WINDOWS:
14+
# C++ compiler flags for GCC and clang
15+
CPPFLAGS= [
16+
# gh-91321: The purpose of _testcppext extension is to check that building
17+
# a C++ extension using the Python C API does not emit C++ compiler
18+
# warnings
19+
'-Werror',
20+
# Warn on old-style cast (C cast) like: (PyObject*)op
21+
'-Wold-style-cast',
22+
# Warn when using NULL rather than _Py_NULL in static inline functions
23+
'-Wzero-as-null-pointer-constant',
24+
]
25+
else:
26+
# Don't pass any compiler flag to MSVC
27+
CPPFLAGS= []
28+
29+
30+
defmain():
31+
cppflags=list(CPPFLAGS)
32+
if'-std=c++03'insys.argv:
33+
sys.argv.remove('-std=c++03')
34+
std='c++03'
35+
name='_testcpp03ext'
36+
else:
37+
# Python currently targets C++11
38+
std='c++11'
39+
name='_testcpp11ext'
40+
41+
cppflags= [*CPPFLAGS,f'-std={std}']
42+
cpp_ext=Extension(
43+
name,
44+
sources=[SOURCE],
45+
language='c++',
46+
extra_compile_args=cppflags)
47+
setup(name=name,ext_modules=[cpp_ext])
48+
49+
50+
if__name__=="__main__":
51+
main()

‎Lib/test/support/__init__.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,3 +2196,20 @@ def clear_ignored_deprecations(*tokens: object) -> None:
21962196
ifwarnings.filters!=new_filters:
21972197
warnings.filters[:]=new_filters
21982198
warnings._filters_mutated()
2199+
2200+
2201+
# Skip a test if venv with pip is known to not work.
2202+
defrequires_venv_with_pip():
2203+
# ensurepip requires zlib to open ZIP archives (.whl binary wheel packages)
2204+
try:
2205+
importzlib
2206+
exceptImportError:
2207+
returnunittest.skipIf(True,"venv: ensurepip requires zlib")
2208+
2209+
# bpo-26610: pip/pep425tags.py requires ctypes.
2210+
# gh-92820: setuptools/windows_support.py uses ctypes (setuptools 58.1).
2211+
try:
2212+
importctypes
2213+
exceptImportError:
2214+
ctypes=None
2215+
returnunittest.skipUnless(ctypes,'venv: pip requires ctypes')

‎Lib/test/test_cppext.py‎

Lines changed: 48 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,73 @@
11
# gh-91321: Build a basic C++ test extension to check that the Python C API is
22
# compatible with C++ and does not emit C++ compiler warnings.
3-
importcontextlib
4-
importos
3+
importos.path
54
importsys
65
importunittest
7-
importwarnings
6+
importsubprocess
87
fromtestimportsupport
98
fromtest.supportimportos_helper
109

11-
withwarnings.catch_warnings():
12-
warnings.simplefilter('ignore',DeprecationWarning)
13-
fromdistutils.coreimportsetup,Extension
14-
importdistutils.sysconfig
15-
1610

1711
MS_WINDOWS= (sys.platform=='win32')
1812

1913

20-
SOURCE=support.findfile('_testcppext.cpp')
21-
ifnotMS_WINDOWS:
22-
# C++ compiler flags for GCC and clang
23-
CPPFLAGS= [
24-
# Python currently targets C++11
25-
'-std=c++11',
26-
# gh-91321: The purpose of _testcppext extension is to check that building
27-
# a C++ extension using the Python C API does not emit C++ compiler
28-
# warnings
29-
'-Werror',
30-
# Warn on old-style cast (C cast) like: (PyObject*)op
31-
'-Wold-style-cast',
32-
# Warn when using NULL rather than _Py_NULL in static inline functions
33-
'-Wzero-as-null-pointer-constant',
34-
]
35-
else:
36-
# Don't pass any compiler flag to MSVC
37-
CPPFLAGS= []
14+
SETUP_TESTCPPEXT=support.findfile('setup_testcppext.py')
3815

3916

4017
@support.requires_subprocess()
4118
classTestCPPExt(unittest.TestCase):
42-
defbuild(self):
43-
cpp_ext=Extension(
44-
'_testcppext',
45-
sources=[SOURCE],
46-
language='c++',
47-
extra_compile_args=CPPFLAGS)
48-
capture_stdout= (notsupport.verbose)
19+
deftest_build_cpp11(self):
20+
self.check_build(False)
4921

50-
try:
51-
try:
52-
ifcapture_stdout:
53-
stdout=support.captured_stdout()
54-
else:
55-
print()
56-
stdout=contextlib.nullcontext()
57-
with (stdout,
58-
support.swap_attr(sys,'argv', ['setup.py','build_ext','--verbose'])):
59-
setup(name="_testcppext",ext_modules=[cpp_ext])
60-
return
61-
except:
62-
ifcapture_stdout:
63-
# Show output on error
64-
print()
65-
print(stdout.getvalue())
66-
raise
67-
exceptSystemExit:
68-
self.fail("Build failed")
22+
deftest_build_cpp03(self):
23+
self.check_build(True)
6924

7025
# With MSVC, the linker fails with: cannot open file 'python311.lib'
7126
# https://github.com/python/cpython/pull/32175#issuecomment-1111175897
7227
@unittest.skipIf(MS_WINDOWS,'test fails on Windows')
73-
deftest_build(self):
74-
# save/restore os.environ
75-
defrestore_env(old_env):
76-
os.environ.clear()
77-
os.environ.update(old_env)
78-
self.addCleanup(restore_env,dict(os.environ))
79-
80-
defrestore_sysconfig_vars(old_config_vars):
81-
distutils.sysconfig._config_vars.clear()
82-
distutils.sysconfig._config_vars.update(old_config_vars)
83-
self.addCleanup(restore_sysconfig_vars,
84-
dict(distutils.sysconfig._config_vars))
85-
28+
# the test uses venv+pip: skip if it's not available
29+
@support.requires_venv_with_pip()
30+
defcheck_build(self,std_cpp03):
8631
# Build in a temporary directory
8732
withos_helper.temp_cwd():
88-
self.build()
33+
self._check_build(std_cpp03)
34+
35+
def_check_build(self,std_cpp03):
36+
venv_dir='env'
37+
verbose=support.verbose
38+
39+
# Create virtual environment to get setuptools
40+
cmd= [sys.executable,'-X','dev','-m','venv',venv_dir]
41+
ifverbose:
42+
print()
43+
print('Run:',' '.join(cmd))
44+
subprocess.run(cmd,check=True)
45+
46+
# Get the Python executable of the venv
47+
python_exe='python'
48+
ifsys.executable.endswith('.exe'):
49+
python_exe+='.exe'
50+
ifMS_WINDOWS:
51+
python=os.path.join(venv_dir,'Scripts',python_exe)
52+
else:
53+
python=os.path.join(venv_dir,'bin',python_exe)
54+
55+
# Build the C++ extension
56+
cmd= [python,'-X','dev',
57+
SETUP_TESTCPPEXT,'build_ext','--verbose']
58+
ifstd_cpp03:
59+
cmd.append('-std=c++03')
60+
ifverbose:
61+
print('Run:',' '.join(cmd))
62+
subprocess.run(cmd,check=True)
63+
else:
64+
proc=subprocess.run(cmd,
65+
stdout=subprocess.PIPE,
66+
stderr=subprocess.STDOUT,
67+
text=True)
68+
ifproc.returncode:
69+
print(proc.stdout,end='')
70+
self.fail(f"Build failed with exit code{proc.returncode}")
8971

9072

9173
if__name__=="__main__":

‎Lib/test/test_venv.py‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
importtempfile
1818
fromtest.supportimport (captured_stdout,captured_stderr,requires_zlib,
1919
skip_if_broken_multiprocessing_synchronize,verbose,
20-
requires_subprocess,is_emscripten,is_wasi)
20+
requires_subprocess,is_emscripten,is_wasi,
21+
requires_venv_with_pip)
2122
fromtest.support.os_helperimport (can_symlink,EnvironmentVarGuard,rmtree)
2223
importunittest
2324
importvenv
@@ -619,9 +620,7 @@ def do_test_with_pip(self, system_site_packages):
619620
ifnotsystem_site_packages:
620621
self.assert_pip_not_installed()
621622

622-
# Issue #26610: pip/pep425tags.py requires ctypes
623-
@unittest.skipUnless(ctypes,'pip requires ctypes')
624-
@requires_zlib()
623+
@requires_venv_with_pip()
625624
deftest_with_pip(self):
626625
self.do_test_with_pip(False)
627626
self.do_test_with_pip(True)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the compatibility of the Python C API with C++ older than C++11. Patch by
2+
Victor Stinner.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp