From 32bea975709108da35c0edde6a5c82e368919cdd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:07:34 +0200 Subject: [PATCH 01/13] _Py_CAST() uses old-style cast on old Python versions _Py_CAST() uses old-style cast on old Python versions to prevent C++ warnings. --- pythoncapi_compat.h | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 72f6ba1..9c688dd 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -34,34 +34,7 @@ extern "C" { // C++ compatibility: _Py_CAST() and _Py_NULL #ifndef _Py_CAST -# ifdef __cplusplus -extern "C++" { - namespace { - template - inline type _Py_CAST_impl(expr_type *expr) { - return reinterpret_cast(expr); - } - - template - inline type _Py_CAST_impl(expr_type const *expr) { - return reinterpret_cast(const_cast(expr)); - } - - template - inline type _Py_CAST_impl(expr_type &expr) { - return static_cast(expr); - } - - template - inline type _Py_CAST_impl(expr_type const &expr) { - return static_cast(const_cast(expr)); - } - } -} -# define _Py_CAST(type, expr) _Py_CAST_impl(expr) -# else -# define _Py_CAST(type, expr) ((type)(expr)) -# endif +# define _Py_CAST(type, expr) ((type)(expr)) #endif #ifndef _Py_NULL # ifdef __cplusplus From 5cab0830a41878bb8371a117da737261a85d736e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:09:26 +0200 Subject: [PATCH 02/13] Fix _Py_NULL on C++ older than C++11 --- pythoncapi_compat.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 9c688dd..c971d10 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -32,12 +32,14 @@ extern "C" { #endif -// C++ compatibility: _Py_CAST() and _Py_NULL #ifndef _Py_CAST # define _Py_CAST(type, expr) ((type)(expr)) #endif + +// On C++11 and newer, _Py_NULL is defined as nullptr on C++11, +// otherwise it is defined as NULL. #ifndef _Py_NULL -# ifdef __cplusplus +# if defined(__cplusplus) && __cplusplus >= 201103 # define _Py_NULL nullptr # else # define _Py_NULL NULL From b7ec4999874a5063a85f4062a555a316f3fb61b3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:09:55 +0200 Subject: [PATCH 03/13] Test compatibility with C++03 --- docs/api.rst | 2 +- docs/changelog.rst | 1 + tests/setup.py | 20 +++++++++++------- tests/test_pythoncapi_compat.py | 15 ++++++-------- tests/test_pythoncapi_compat_cext.c | 32 ++++++++++++++++++++--------- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 77bc94b..e64d7fe 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -10,7 +10,7 @@ Supported Python versions: * Python 2.7, Python 3.4 - 3.11 * PyPy 2.7, 3.6 and 3.7 -C++ is supported on Python 3.6 and newer. +C++03 and C++11 are supported on Python 3.6 and newer. A C99 subset is required, like ``static inline`` functions: see `PEP 7 `_. ISO C90 is partially supported diff --git a/docs/changelog.rst b/docs/changelog.rst index ecb5565..8faf882 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* 2022-06-14: Fix compatibility with C++ older than C++11. * 2022-05-03: Add ``PyCode_GetCode()`` function. * 2022-04-26: Rename the project from ``pythoncapi_compat`` to ``pythoncapi-compat``: replace the underscore separator with a dash. diff --git a/tests/setup.py b/tests/setup.py index 0e888c1..9b5a628 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -57,13 +57,19 @@ def main(): extensions = [c_ext] if TEST_CPP: - # C++ extension - cpp_ext = Extension( - 'test_pythoncapi_compat_cppext', - sources=['test_pythoncapi_compat_cppext.cpp'], - extra_compile_args=cppflags, - language='c++') - extensions.append(cpp_ext) + for name, std in ( + ('test_pythoncapi_compat_cpp03ext', 'c++03'), + ('test_pythoncapi_compat_cpp11ext', 'c++11'), + ): + # C++ extension + flags = list(cppflags) + flags.append('-std=' + std) + cpp_ext = Extension( + name, + sources=['test_pythoncapi_compat_cppext.cpp'], + extra_compile_args=flags, + language='c++') + extensions.append(cpp_ext) setup(name="test_pythoncapi_compat", ext_modules=extensions) diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index d23a943..af863a6 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -98,11 +98,7 @@ def _check_refleak(test_func, verbose): raise AssertionError("refcnt leak, diff: %s" % diff) -def run_tests(module_name): - if "cppext" in module_name: - lang = "C++" - else: - lang = "C" +def run_tests(module_name, lang): title = "Test %s (%s)" % (module_name, lang) display_title(title) @@ -150,9 +146,9 @@ def main(): VERBOSE = ("-v" in sys.argv[1:] or "--verbose" in sys.argv[1:]) # Implementing PyFrame_GetLocals() and PyCode_GetCode() require the - # internal C API in Python 3.11 alpha versions. Skip also Python 3.11b1 + # internal C API in Python 3.11 alpha versions. Skip also Python 3.11b3 # which has issues with C++ casts: _Py_CAST() macro. - if 0x30b0000 <= sys.hexversion <= 0x30b00b1: + if 0x30b0000 <= sys.hexversion <= 0x30b00b3: version = sys.version.split()[0] print("SKIP TESTS: Python %s is not supported" % version) return @@ -166,9 +162,10 @@ def main(): build_ext() - run_tests("test_pythoncapi_compat_cext") + run_tests("test_pythoncapi_compat_cext", "C") if TEST_CPP: - run_tests("test_pythoncapi_compat_cppext") + run_tests("test_pythoncapi_compat_cpp03ext", "C++03") + run_tests("test_pythoncapi_compat_cpp11ext", "C++11") if __name__ == "__main__": diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index 2ca9726..db5e16c 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -11,14 +11,21 @@ # define PYTHON3 1 #endif -#ifdef __cplusplus -# define MODULE_NAME test_pythoncapi_compat_cppext -# define MODULE_NAME_STR "test_pythoncapi_compat_cppext" +#if defined(__cplusplus) && __cplusplus >= 201103 +# define MODULE_NAME test_pythoncapi_compat_cpp11ext +#elif defined(__cplusplus) +# define MODULE_NAME test_pythoncapi_compat_cpp03ext #else # define MODULE_NAME test_pythoncapi_compat_cext -# define MODULE_NAME_STR "test_pythoncapi_compat_cext" #endif +#define _STR(NAME) #NAME +#define STR(NAME) _STR(NAME) +#define _CONCAT(a, b) a ## b +#define CONCAT(a, b) _CONCAT(a, b) + +#define MODULE_NAME_STR STR(MODULE_NAME) + // Ignore reference count checks on PyPy #if !defined(PYPY_VERSION) # define CHECK_REFCNT @@ -30,8 +37,6 @@ # define ASSERT_REFCNT(expr) #endif -#define CONCAT(a, b) a ## b - static PyObject * test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) { @@ -539,6 +544,15 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_DECREF(strong_ref); #endif + // gh-93442: Pass 0 as NULL for PyObject* + Py_XINCREF(0); + Py_XDECREF(0); +#if _cplusplus >= 201103 + // Test nullptr passed as PyObject* + Py_XINCREF(nullptr); + Py_XDECREF(nullptr); +#endif + Py_DECREF(obj); Py_RETURN_NONE; } @@ -584,8 +598,7 @@ static struct PyModuleDef module = { }; -#define _INIT_FUNC(name) CONCAT(PyInit_, name) -#define INIT_FUNC _INIT_FUNC(MODULE_NAME) +#define INIT_FUNC CONCAT(PyInit_, MODULE_NAME) #if PY_VERSION_HEX >= 0x03050000 PyMODINIT_FUNC @@ -605,8 +618,7 @@ INIT_FUNC(void) #else // Python 2 -#define _INIT_FUNC(name) CONCAT(init, name) -#define INIT_FUNC _INIT_FUNC(MODULE_NAME) +#define INIT_FUNC CONCAT(init, MODULE_NAME) PyMODINIT_FUNC INIT_FUNC(void) From bcb742d50783d52e0e09aa62b4ea5c10384299d0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:19:30 +0200 Subject: [PATCH 04/13] Enable C tests on Python 3.11b1 --- tests/test_pythoncapi_compat.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index af863a6..da33e18 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -25,6 +25,10 @@ # C++ is only supported on Python 3.6 and newer TEST_CPP = (sys.version_info >= (3, 6)) +if 0x30b0000 <= sys.hexversion <= 0x30b00b3: + # Don't test C++ on Python 3.11b1 - 3.11b3: these versions have C++ + # compatibility issues. + TEST_CPP = False VERBOSE = False @@ -146,9 +150,8 @@ def main(): VERBOSE = ("-v" in sys.argv[1:] or "--verbose" in sys.argv[1:]) # Implementing PyFrame_GetLocals() and PyCode_GetCode() require the - # internal C API in Python 3.11 alpha versions. Skip also Python 3.11b3 - # which has issues with C++ casts: _Py_CAST() macro. - if 0x30b0000 <= sys.hexversion <= 0x30b00b3: + # internal C API in Python 3.11 alpha versions. + if 0x30b0000 <= sys.hexversion < 0x30b00b1: version = sys.version.split()[0] print("SKIP TESTS: Python %s is not supported" % version) return From e88cfdb9d20f57d7c1676914303b9e5dc703c306 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:20:54 +0200 Subject: [PATCH 05/13] GHA: update Python 3.11 to 3.11b3 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65e56c7..2a4dd8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - "3.8" - "3.9" - "3.10" - - "3.11.0-alpha.5" + - "3.11.0-beta.3" - "pypy2" - "pypy3" include: From 522fde10fcf53dfe21e7f848c62620ee24a93eae Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:33:20 +0200 Subject: [PATCH 06/13] Doc: mention C++ --- README.rst | 7 ++++--- docs/index.rst | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 4f3e560..800c5df 100644 --- a/README.rst +++ b/README.rst @@ -6,9 +6,10 @@ Python C API compatibility :alt: Build status of pythoncapi-compat on GitHub Actions :target: https://github.com/python/pythoncapi-compat/actions -The ``pythoncapi-compat`` project can be used to write a C extension supporting -a wide range of Python versions with a single code base. It is made of the -``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` script. +The ``pythoncapi-compat`` project can be used to write a C or C++ extension +supporting a wide range of Python versions with a single code base. It is made +of the ``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` +script. ``upgrade_pythoncapi.py`` requires Python 3.6 or newer. diff --git a/docs/index.rst b/docs/index.rst index 86cf164..daf3490 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,9 +2,10 @@ Python C API compatibility ++++++++++++++++++++++++++ -The ``pythoncapi-compat`` project can be used to write a C extension supporting -a wide range of Python versions with a single code base. It is made of the -``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` script. +The ``pythoncapi-compat`` project can be used to write a C or C++ extension +supporting a wide range of Python versions with a single code base. It is made +of the ``pythoncapi_compat.h`` header file and the ``upgrade_pythoncapi.py`` +script. * Homepage: `GitHub pythoncapi-compat project `_. From 954f276dcabc097eb1eb76cab67a7189b782dcea Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:33:30 +0200 Subject: [PATCH 07/13] Don't test C++03 with MSVC MSVC has no /std:c++03 flag. --- tests/setup.py | 20 ++++++++++++-------- tests/test_pythoncapi_compat.py | 10 +++++++++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/setup.py b/tests/setup.py index 9b5a628..461cdf6 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -6,6 +6,9 @@ TEST_CPP = False SRC_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) +# Windows uses MSVC compiler +MSVC = (os.name == "nt") + # C compiler flags for GCC and clang COMMON_FLAGS = [ # Treat warnings as error @@ -44,8 +47,7 @@ def main(): cflags = ['-I' + SRC_DIR] cppflags = list(cflags) - # Windows uses MSVC compiler - if os.name != "nt": + if not MSVC: cflags.extend(CFLAGS) cppflags.extend(CPPFLAGS) @@ -57,13 +59,15 @@ def main(): extensions = [c_ext] if TEST_CPP: - for name, std in ( - ('test_pythoncapi_compat_cpp03ext', 'c++03'), - ('test_pythoncapi_compat_cpp11ext', 'c++11'), - ): - # C++ extension + # C++ extension + versions = [('test_pythoncapi_compat_cpp11ext', 'c++11')] + if not MSVC: + versions.append(('test_pythoncapi_compat_cpp03ext', 'c++03')) + for name, std in versions: flags = list(cppflags) - flags.append('-std=' + std) + # MSVC has /std flag but doesn't support /std:c++11 + if not MSVC: + flags.append('-std=' + std) cpp_ext = Extension( name, sources=['test_pythoncapi_compat_cppext.cpp'], diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index da33e18..d67c349 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -23,12 +23,19 @@ from utils import run_command, command_stdout +# Windows uses MSVC compiler +MSVC = (os.name == "nt") + # C++ is only supported on Python 3.6 and newer TEST_CPP = (sys.version_info >= (3, 6)) if 0x30b0000 <= sys.hexversion <= 0x30b00b3: # Don't test C++ on Python 3.11b1 - 3.11b3: these versions have C++ # compatibility issues. TEST_CPP = False +# test_pythoncapi_compat_cpp03ext is not built with MSVC +TEST_CPP03 = (not MSVC) +if not TEST_CPP: + TEST_CPP03 = False VERBOSE = False @@ -167,7 +174,8 @@ def main(): run_tests("test_pythoncapi_compat_cext", "C") if TEST_CPP: - run_tests("test_pythoncapi_compat_cpp03ext", "C++03") + if TEST_CPP03: + run_tests("test_pythoncapi_compat_cpp03ext", "C++03") run_tests("test_pythoncapi_compat_cpp11ext", "C++11") From dfb8e840a37e898bcf351d870a86f572cfe1ede1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:48:52 +0200 Subject: [PATCH 08/13] runtests.py --verbose logs __cplusplus value --- tests/test_pythoncapi_compat.py | 2 + tests/test_pythoncapi_compat_cext.c | 64 ++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index d67c349..cd028d5 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -114,6 +114,8 @@ def run_tests(module_name, lang): display_title(title) testmod = import_tests(module_name) + if VERBOSE and hasattr(testmod, "__cplusplus"): + print("__cplusplus: %s" % testmod.__cplusplus) check_refleak = hasattr(sys, 'gettotalrefcount') diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index db5e16c..5abee29 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -580,6 +580,32 @@ static struct PyMethodDef methods[] = { }; +#ifdef __cplusplus +static int +module_exec(PyObject *module) +{ + if (PyModule_AddIntMacro(module, __cplusplus)) { + return -1; + } + return 0; +} +#else +static int +module_exec(PyObject *Py_UNUSED(module)) +{ + return 0; +} +#endif + + +#if PY_VERSION_HEX >= 0x03050000 +static PyModuleDef_Slot module_slots[] = { + {Py_mod_exec, _Py_CAST(void*, module_exec)}, + {0, _Py_NULL} +}; +#endif + + #ifdef PYTHON3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, @@ -588,13 +614,13 @@ static struct PyModuleDef module = { 0, // m_doc methods, // m_methods #if PY_VERSION_HEX >= 0x03050000 - _Py_NULL, // m_slots + module_slots, // m_slots #else - _Py_NULL, // m_reload + _Py_NULL, // m_reload #endif - _Py_NULL, // m_traverse - _Py_NULL, // m_clear - _Py_NULL, // m_free + _Py_NULL, // m_traverse + _Py_NULL, // m_clear + _Py_NULL, // m_free }; @@ -611,7 +637,15 @@ INIT_FUNC(void) PyMODINIT_FUNC INIT_FUNC(void) { - return PyModule_Create(&module); + PyObject *module = PyModule_Create(&module); + if (module == NULL) { + return NULL; + } + if (module_exec(module) < 0) { + Py_DECREF(module); + return NULL; + } + return module; } #endif @@ -623,10 +657,18 @@ INIT_FUNC(void) PyMODINIT_FUNC INIT_FUNC(void) { - Py_InitModule4(MODULE_NAME_STR, - methods, - _Py_NULL, - _Py_NULL, - PYTHON_API_VERSION); + PyObject *module; + module = Py_InitModule4(MODULE_NAME_STR, + methods, + _Py_NULL, + _Py_NULL, + PYTHON_API_VERSION); + if (module == NULL) { + return; + } + + if (module_exec(module) < 0) { + return; + } } #endif From ef87cb494e0866dec127a93faf1c6126b63543d8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:52:31 +0200 Subject: [PATCH 09/13] Fix tests for MSVC --- tests/setup.py | 18 ++++++++++++------ tests/test_pythoncapi_compat.py | 17 ++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/setup.py b/tests/setup.py index 461cdf6..60f89c1 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -60,14 +60,20 @@ def main(): if TEST_CPP: # C++ extension - versions = [('test_pythoncapi_compat_cpp11ext', 'c++11')] + + # MSVC has /std flag but doesn't support /std:c++11 if not MSVC: - versions.append(('test_pythoncapi_compat_cpp03ext', 'c++03')) - for name, std in versions: + versions = [ + ('test_pythoncapi_compat_cpp03ext', '-std=c++03'), + ('test_pythoncapi_compat_cpp11ext', '-std=c++11'), + ] + else: + versions = [ + ('test_pythoncapi_compat_cpp11ext', '/std:c++14'), + ] + for name, flag in versions: flags = list(cppflags) - # MSVC has /std flag but doesn't support /std:c++11 - if not MSVC: - flags.append('-std=' + std) + flags.append(flag) cpp_ext = Extension( name, sources=['test_pythoncapi_compat_cppext.cpp'], diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index cd028d5..0ea400c 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -34,8 +34,14 @@ TEST_CPP = False # test_pythoncapi_compat_cpp03ext is not built with MSVC TEST_CPP03 = (not MSVC) -if not TEST_CPP: - TEST_CPP03 = False + +TESTS = [("test_pythoncapi_compat_cext", "C")] +if TEST_CPP: + if TEST_CPP03: + TESTS.append(("test_pythoncapi_compat_cpp03ext", "C++03")) + TESTS.append(("test_pythoncapi_compat_cpp11ext", "C++11")) + + VERBOSE = False @@ -174,11 +180,8 @@ def main(): build_ext() - run_tests("test_pythoncapi_compat_cext", "C") - if TEST_CPP: - if TEST_CPP03: - run_tests("test_pythoncapi_compat_cpp03ext", "C++03") - run_tests("test_pythoncapi_compat_cpp11ext", "C++11") + for module_name, lang in TESTS: + run_tests(module_name, lang) if __name__ == "__main__": From 6061795fce9be0f182337acb69e4ad2ec2a4dc9d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 15:55:18 +0200 Subject: [PATCH 10/13] Fix Python 3.4 --- tests/test_pythoncapi_compat_cext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index 5abee29..66a047a 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -607,7 +607,7 @@ static PyModuleDef_Slot module_slots[] = { #ifdef PYTHON3 -static struct PyModuleDef module = { +static struct PyModuleDef module_def = { PyModuleDef_HEAD_INIT, MODULE_NAME_STR, // m_name _Py_NULL, // m_doc @@ -630,14 +630,14 @@ static struct PyModuleDef module = { PyMODINIT_FUNC INIT_FUNC(void) { - return PyModuleDef_Init(&module); + return PyModuleDef_Init(&module_def); } #else // Python 3.4 PyMODINIT_FUNC INIT_FUNC(void) { - PyObject *module = PyModule_Create(&module); + PyObject *module = PyModule_Create(&module_def); if (module == NULL) { return NULL; } From a11300122fcd0e7a493be4df0cc14f2d7366cdc7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 16:03:55 +0200 Subject: [PATCH 11/13] Cleanup test_pythoncapi_compat.py --- tests/setup.py | 17 ++++---- tests/test_pythoncapi_compat.py | 72 +++++++++++++++------------------ 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/tests/setup.py b/tests/setup.py index 60f89c1..2401748 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -3,7 +3,13 @@ import sys -TEST_CPP = False +# C++ is only supported on Python 3.6 and newer +TEST_CPP = (sys.version_info >= (3, 6)) +if 0x30b0000 <= sys.hexversion <= 0x30b00b3: + # Don't test C++ on Python 3.11b1 - 3.11b3: these versions have C++ + # compatibility issues. + TEST_CPP = False + SRC_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) # Windows uses MSVC compiler @@ -40,11 +46,6 @@ def main(): except ImportError: from distutils.core import setup, Extension - if len(sys.argv) >= 3 and sys.argv[2] == '--build-cppext': - global TEST_CPP - TEST_CPP = True - del sys.argv[2] - cflags = ['-I' + SRC_DIR] cppflags = list(cflags) if not MSVC: @@ -69,11 +70,13 @@ def main(): ] else: versions = [ + ('test_pythoncapi_compat_cpp03ext', None), ('test_pythoncapi_compat_cpp11ext', '/std:c++14'), ] for name, flag in versions: flags = list(cppflags) - flags.append(flag) + if flag is not None: + flags.append(flag) cpp_ext = Extension( name, sources=['test_pythoncapi_compat_cppext.cpp'], diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index 0ea400c..a9815e0 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -23,24 +23,11 @@ from utils import run_command, command_stdout -# Windows uses MSVC compiler -MSVC = (os.name == "nt") - -# C++ is only supported on Python 3.6 and newer -TEST_CPP = (sys.version_info >= (3, 6)) -if 0x30b0000 <= sys.hexversion <= 0x30b00b3: - # Don't test C++ on Python 3.11b1 - 3.11b3: these versions have C++ - # compatibility issues. - TEST_CPP = False -# test_pythoncapi_compat_cpp03ext is not built with MSVC -TEST_CPP03 = (not MSVC) - -TESTS = [("test_pythoncapi_compat_cext", "C")] -if TEST_CPP: - if TEST_CPP03: - TESTS.append(("test_pythoncapi_compat_cpp03ext", "C++03")) - TESTS.append(("test_pythoncapi_compat_cpp11ext", "C++11")) - +TESTS = [ + ("test_pythoncapi_compat_cext", "C"), + ("test_pythoncapi_compat_cpp03ext", "C++03"), + ("test_pythoncapi_compat_cpp11ext", "C++11"), +] VERBOSE = False @@ -59,15 +46,10 @@ def display_title(title): def build_ext(): - if TEST_CPP: - display_title("Build the C and C++ extensions") - else: - display_title("Build the C extension") + display_title("Build test extensions") if os.path.exists("build"): shutil.rmtree("build") cmd = [sys.executable, "setup.py", "build"] - if TEST_CPP: - cmd.append('--build-cppext') if VERBOSE: run_command(cmd) print() @@ -115,11 +97,36 @@ def _check_refleak(test_func, verbose): raise AssertionError("refcnt leak, diff: %s" % diff) +def python_version(): + ver = sys.version_info + build = 'debug' if hasattr(sys, 'gettotalrefcount') else 'release' + if hasattr(sys, 'implementation'): + python_impl = sys.implementation.name + if python_impl == 'cpython': + python_impl = 'CPython' + elif python_impl == 'pypy': + python_impl = 'PyPy' + else: + if "PyPy" in sys.version: + python_impl = "PyPy" + else: + python_impl = 'Python' + return "%s %s.%s (%s build)" % (python_impl, ver.major, ver.minor, build) + + def run_tests(module_name, lang): title = "Test %s (%s)" % (module_name, lang) display_title(title) - testmod = import_tests(module_name) + try: + testmod = import_tests(module_name) + except ImportError: + print("%s: skip %s, missing %s extension" + % (python_version(), lang, module_name)) + if VERBOSE: + print() + return + if VERBOSE and hasattr(testmod, "__cplusplus"): print("__cplusplus: %s" % testmod.__cplusplus) @@ -140,21 +147,8 @@ def test_func(): if VERBOSE: print() - ver = sys.version_info - build = 'debug' if hasattr(sys, 'gettotalrefcount') else 'release' msg = "%s %s tests succeeded!" % (len(tests), lang) - if hasattr(sys, 'implementation'): - python_impl = sys.implementation.name - if python_impl == 'cpython': - python_impl = 'CPython' - elif python_impl == 'pypy': - python_impl = 'PyPy' - else: - if "PyPy" in sys.version: - python_impl = "PyPy" - else: - python_impl = 'Python' - msg = "%s %s.%s (%s build): %s" % (python_impl, ver.major, ver.minor, build, msg) + msg = "%s: %s" % (python_version(), msg) if check_refleak: msg = "%s (no reference leak detected)" % msg print(msg) From 4c9d49d6cf8a16f02c4b63d3578b4ddc4fa41e9d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 16:15:31 +0200 Subject: [PATCH 12/13] MSC: remove C++ version from the C++ extension name --- tests/setup.py | 2 +- tests/test_pythoncapi_compat.py | 9 +++++++-- tests/test_pythoncapi_compat_cext.c | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/setup.py b/tests/setup.py index 2401748..a22ab30 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -70,7 +70,7 @@ def main(): ] else: versions = [ - ('test_pythoncapi_compat_cpp03ext', None), + ('test_pythoncapi_compat_cppext', None), ('test_pythoncapi_compat_cpp11ext', '/std:c++14'), ] for name, flag in versions: diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index a9815e0..0b1a4d0 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -25,6 +25,7 @@ TESTS = [ ("test_pythoncapi_compat_cext", "C"), + ("test_pythoncapi_compat_cppext", "C++"), ("test_pythoncapi_compat_cpp03ext", "C++03"), ("test_pythoncapi_compat_cpp11ext", "C++11"), ] @@ -121,9 +122,13 @@ def run_tests(module_name, lang): try: testmod = import_tests(module_name) except ImportError: - print("%s: skip %s, missing %s extension" - % (python_version(), lang, module_name)) + # The C extension must always be available + if lang == "C": + raise + if VERBOSE: + print("%s: skip %s, missing %s extension" + % (python_version(), lang, module_name)) print() return diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index 66a047a..46c8bde 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -11,7 +11,9 @@ # define PYTHON3 1 #endif -#if defined(__cplusplus) && __cplusplus >= 201103 +#if defined(_MSC_VER) && defined(__cplusplus) +# define MODULE_NAME test_pythoncapi_compat_cppext +#elif defined(__cplusplus) && __cplusplus >= 201103 # define MODULE_NAME test_pythoncapi_compat_cpp11ext #elif defined(__cplusplus) # define MODULE_NAME test_pythoncapi_compat_cpp03ext From 0557788d44d82f701f1d7c80f6a0fcf607226783 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2022 16:23:11 +0200 Subject: [PATCH 13/13] Fix MSC --- tests/setup.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/setup.py b/tests/setup.py index a22ab30..4a0cc96 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -69,10 +69,7 @@ def main(): ('test_pythoncapi_compat_cpp11ext', '-std=c++11'), ] else: - versions = [ - ('test_pythoncapi_compat_cppext', None), - ('test_pythoncapi_compat_cpp11ext', '/std:c++14'), - ] + versions = [('test_pythoncapi_compat_cppext', None)] for name, flag in versions: flags = list(cppflags) if flag is not None: