From 4907adfdb18d6a8d2ba7e92879080815efa12d1c Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 27 Dec 2021 19:28:05 -0800 Subject: [PATCH 1/5] Adding dedicated test_const_name. Also exercises pybind11::detail::_ backward compatibility. See also: PR #3423 --- include/pybind11/detail/descr.h | 9 +++-- tests/CMakeLists.txt | 1 + tests/test_const_name.cpp | 63 ++++++++++++++++++++++++++++++ tests/test_const_name.py | 40 +++++++++++++++++++ tests/test_custom_type_casters.cpp | 2 +- 5 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 tests/test_const_name.cpp create mode 100644 tests/test_const_name.py diff --git a/include/pybind11/detail/descr.h b/include/pybind11/detail/descr.h index 14f9223a50..da27b60ddc 100644 --- a/include/pybind11/detail/descr.h +++ b/include/pybind11/detail/descr.h @@ -84,9 +84,12 @@ auto constexpr const_name() -> remove_cv_t constexpr descr<1, Type> const_name() { return {'%'}; } -// The "_" might be defined as a macro - don't define it if so. -// Repeating the const_name code to avoid introducing a #define. +// If "_" is defined as a macro, py::detail::_ cannot be provided. +// It is therefore best to use py::detail::const_name universally. +// This block is for backward compatibility only. +// (The const_name code is repeated to avoid introducing a "_" #define ourselves.) #ifndef _ +#define PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY template constexpr descr _(char const(&text)[N]) { return const_name(text); } template @@ -107,7 +110,7 @@ auto constexpr _() -> remove_cv_t::dig return const_name(); } template constexpr descr<1, Type> _() { return const_name(); } -#endif +#endif // #ifndef _ constexpr descr<0> concat() { return {}; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b19b19670..b00c043ac3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -101,6 +101,7 @@ set(PYBIND11_TEST_FILES test_callbacks.cpp test_chrono.cpp test_class.cpp + test_const_name.cpp test_constants_and_functions.cpp test_copy_move.cpp test_custom_type_casters.cpp diff --git a/tests/test_const_name.cpp b/tests/test_const_name.cpp new file mode 100644 index 0000000000..517bddf337 --- /dev/null +++ b/tests/test_const_name.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2021 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "pybind11_tests.h" + +// IUT = Implementation Under Test +#define CONST_NAME_TESTS(TEST_FUNC, IUT) \ + std::string TEST_FUNC(int selector) { \ + switch (selector) { \ + case 0: \ + return IUT("").text; \ + case 1: \ + return IUT("A").text; \ + case 2: \ + return IUT("Bd").text; \ + case 3: \ + return IUT("Cef").text; \ + case 4: \ + return IUT().text; \ + case 5: \ + return IUT().text; \ + case 6: \ + return IUT<0>().text; \ + case 7: \ + return IUT<1>().text; \ + case 8: \ + return IUT<2>().text; \ + case 9: \ + return IUT<34>().text; \ + case 10: \ + return IUT<567>().text; \ + case 11: \ + return IUT("T1", "T2").text; \ + case 12: \ + return IUT("U1", "U2").text; \ + case 13: \ + return IUT(IUT("D1"), IUT("D2")).text; \ + case 14: \ + return IUT(IUT("E1"), IUT("E2")).text; \ + case 15: \ + return IUT("KeepAtEnd").text; \ + default: \ + break; \ + } \ + throw std::runtime_error("Invalid selector value."); \ + } + +CONST_NAME_TESTS(const_name_tests, py::detail::const_name) + +#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY +CONST_NAME_TESTS(underscore_tests, py::detail::_) +#endif + +TEST_SUBMODULE(const_name, m) { + m.def("const_name_tests", const_name_tests); + +#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY + m.def("underscore_tests", underscore_tests); +#else + m.attr("underscore_tests") = py::none(); +#endif +} diff --git a/tests/test_const_name.py b/tests/test_const_name.py new file mode 100644 index 0000000000..328300440c --- /dev/null +++ b/tests/test_const_name.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import itertools + +import pytest + +from pybind11_tests import const_name as m + + +@pytest.mark.parametrize( + "func, selector_expected", + itertools.product( + (m.const_name_tests, m.underscore_tests), + enumerate( + ( + "", + "A", + "Bd", + "Cef", + "%", + "%", + "0", + "1", + "2", + "34", + "567", + "T1", + "U2", + "D1", + "E2", + "KeepAtEnd", + ) + ), + ), +) +def test_const_name(func, selector_expected): + selector, expected = selector_expected + if func is None: + pytest.skip("PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.") + text = func(selector) + assert text == expected diff --git a/tests/test_custom_type_casters.cpp b/tests/test_custom_type_casters.cpp index 8ad584de6d..48613ee5a1 100644 --- a/tests/test_custom_type_casters.cpp +++ b/tests/test_custom_type_casters.cpp @@ -19,7 +19,7 @@ namespace pybind11 { namespace detail { template <> struct type_caster { public: // Classic -#ifndef _ +#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY PYBIND11_TYPE_CASTER(ArgInspector1, _("ArgInspector1")); #else PYBIND11_TYPE_CASTER(ArgInspector1, const_name("ArgInspector1")); From 0f763169458dbc71a5d461f753bb9126bab1ffc9 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 28 Dec 2021 14:40:14 -0800 Subject: [PATCH 2/5] Backing out tests involving int_to_str (requires C++17 or higher). --- include/pybind11/detail/descr.h | 1 + tests/test_const_name.cpp | 18 ++++-------------- tests/test_const_name.py | 5 ----- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/include/pybind11/detail/descr.h b/include/pybind11/detail/descr.h index da27b60ddc..0f93e06b21 100644 --- a/include/pybind11/detail/descr.h +++ b/include/pybind11/detail/descr.h @@ -59,6 +59,7 @@ constexpr descr<0> const_name(char const(&)[1]) { return {}; } template struct int_to_str : int_to_str { }; template struct int_to_str<0, Digits...> { + // WARNING: This only works with C++17 or higher. static constexpr auto digits = descr(('0' + Digits)...); }; diff --git a/tests/test_const_name.cpp b/tests/test_const_name.cpp index 517bddf337..37fed44ae3 100644 --- a/tests/test_const_name.cpp +++ b/tests/test_const_name.cpp @@ -21,24 +21,14 @@ case 5: \ return IUT().text; \ case 6: \ - return IUT<0>().text; \ - case 7: \ - return IUT<1>().text; \ - case 8: \ - return IUT<2>().text; \ - case 9: \ - return IUT<34>().text; \ - case 10: \ - return IUT<567>().text; \ - case 11: \ return IUT("T1", "T2").text; \ - case 12: \ + case 7: \ return IUT("U1", "U2").text; \ - case 13: \ + case 8: \ return IUT(IUT("D1"), IUT("D2")).text; \ - case 14: \ + case 9: \ return IUT(IUT("E1"), IUT("E2")).text; \ - case 15: \ + case 10: \ return IUT("KeepAtEnd").text; \ default: \ break; \ diff --git a/tests/test_const_name.py b/tests/test_const_name.py index 328300440c..eede09092a 100644 --- a/tests/test_const_name.py +++ b/tests/test_const_name.py @@ -18,11 +18,6 @@ "Cef", "%", "%", - "0", - "1", - "2", - "34", - "567", "T1", "U2", "D1", From 5d11ac355cae0c4c0d02bdf68388424d9cf1a4b6 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 28 Dec 2021 15:18:16 -0800 Subject: [PATCH 3/5] Suppressing clang-tidy errors. --- tests/test_const_name.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_const_name.cpp b/tests/test_const_name.cpp index 37fed44ae3..b9b6f5c92d 100644 --- a/tests/test_const_name.cpp +++ b/tests/test_const_name.cpp @@ -17,16 +17,18 @@ case 3: \ return IUT("Cef").text; \ case 4: \ - return IUT().text; \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ case 5: \ - return IUT().text; \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ case 6: \ - return IUT("T1", "T2").text; \ + return IUT("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ case 7: \ - return IUT("U1", "U2").text; \ + return IUT("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ case 8: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ return IUT(IUT("D1"), IUT("D2")).text; \ case 9: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ return IUT(IUT("E1"), IUT("E2")).text; \ case 10: \ return IUT("KeepAtEnd").text; \ From 9f3a012f28d9f4c7699a20bb1494e1e6d640d647 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 28 Dec 2021 17:17:00 -0800 Subject: [PATCH 4/5] Disabling test_const_name for MSVC 2015 due to bizarre failures. --- tests/test_const_name.cpp | 87 +++++++++++++++++++++++---------------- tests/test_const_name.py | 5 ++- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/tests/test_const_name.cpp b/tests/test_const_name.cpp index b9b6f5c92d..5cb3d16c14 100644 --- a/tests/test_const_name.cpp +++ b/tests/test_const_name.cpp @@ -4,52 +4,67 @@ #include "pybind11_tests.h" +#if defined(_MSC_VER) && _MSC_VER < 1910 + +// MSVC 2015 fails in bizarre ways. +# define PYBIND11_SKIP_TEST_CONST_NAME + +#else // Only test with MSVC 2017 or newer. + // IUT = Implementation Under Test -#define CONST_NAME_TESTS(TEST_FUNC, IUT) \ - std::string TEST_FUNC(int selector) { \ - switch (selector) { \ - case 0: \ - return IUT("").text; \ - case 1: \ - return IUT("A").text; \ - case 2: \ - return IUT("Bd").text; \ - case 3: \ - return IUT("Cef").text; \ - case 4: \ - return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ - case 5: \ - return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ - case 6: \ - return IUT("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ - case 7: \ - return IUT("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ - case 8: \ - /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ - return IUT(IUT("D1"), IUT("D2")).text; \ - case 9: \ - /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ - return IUT(IUT("E1"), IUT("E2")).text; \ - case 10: \ - return IUT("KeepAtEnd").text; \ - default: \ - break; \ - } \ - throw std::runtime_error("Invalid selector value."); \ - } +# define CONST_NAME_TESTS(TEST_FUNC, IUT) \ + std::string TEST_FUNC(int selector) { \ + switch (selector) { \ + case 0: \ + return IUT("").text; \ + case 1: \ + return IUT("A").text; \ + case 2: \ + return IUT("Bd").text; \ + case 3: \ + return IUT("Cef").text; \ + case 4: \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 5: \ + return IUT().text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 6: \ + return IUT("T1", "T2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 7: \ + return IUT("U1", "U2").text; /*NOLINT(bugprone-macro-parentheses)*/ \ + case 8: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ + return IUT(IUT("D1"), IUT("D2")).text; \ + case 9: \ + /*NOLINTNEXTLINE(bugprone-macro-parentheses)*/ \ + return IUT(IUT("E1"), IUT("E2")).text; \ + case 10: \ + return IUT("KeepAtEnd").text; \ + default: \ + break; \ + } \ + throw std::runtime_error("Invalid selector value."); \ + } CONST_NAME_TESTS(const_name_tests, py::detail::const_name) -#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY +# ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY CONST_NAME_TESTS(underscore_tests, py::detail::_) -#endif +# endif + +#endif // MSVC >= 2017 TEST_SUBMODULE(const_name, m) { +#ifdef PYBIND11_SKIP_TEST_CONST_NAME + m.attr("const_name_tests") = "PYBIND11_SKIP_TEST_CONST_NAME"; +#else m.def("const_name_tests", const_name_tests); +#endif -#ifdef PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY +#ifdef PYBIND11_SKIP_TEST_CONST_NAME + m.attr("underscore_tests") = "PYBIND11_SKIP_TEST_CONST_NAME"; +#elif defined(PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY) m.def("underscore_tests", underscore_tests); #else - m.attr("underscore_tests") = py::none(); + m.attr("underscore_tests") = "PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined."; #endif } diff --git a/tests/test_const_name.py b/tests/test_const_name.py index eede09092a..d5ee7b5a6b 100644 --- a/tests/test_const_name.py +++ b/tests/test_const_name.py @@ -3,6 +3,7 @@ import pytest +import env from pybind11_tests import const_name as m @@ -29,7 +30,7 @@ ) def test_const_name(func, selector_expected): selector, expected = selector_expected - if func is None: - pytest.skip("PYBIND11_DETAIL_UNDERSCORE_BACKWARD_COMPATIBILITY not defined.") + if isinstance(func, type(u"") if env.PY2 else str): + pytest.skip(func) text = func(selector) assert text == expected From f1491bb864969a34b2ceeefd541e4a7314fdc34d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 28 Dec 2021 17:23:04 -0800 Subject: [PATCH 5/5] Stacking @pytest.mark.parametrize (thanks to @skylion007 for pointing out). --- tests/test_const_name.py | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/tests/test_const_name.py b/tests/test_const_name.py index d5ee7b5a6b..d4e45e5e98 100644 --- a/tests/test_const_name.py +++ b/tests/test_const_name.py @@ -1,35 +1,30 @@ # -*- coding: utf-8 -*- -import itertools - import pytest import env from pybind11_tests import const_name as m +@pytest.mark.parametrize("func", (m.const_name_tests, m.underscore_tests)) @pytest.mark.parametrize( - "func, selector_expected", - itertools.product( - (m.const_name_tests, m.underscore_tests), - enumerate( - ( - "", - "A", - "Bd", - "Cef", - "%", - "%", - "T1", - "U2", - "D1", - "E2", - "KeepAtEnd", - ) - ), + "selector, expected", + enumerate( + ( + "", + "A", + "Bd", + "Cef", + "%", + "%", + "T1", + "U2", + "D1", + "E2", + "KeepAtEnd", + ) ), ) -def test_const_name(func, selector_expected): - selector, expected = selector_expected +def test_const_name(func, selector, expected): if isinstance(func, type(u"") if env.PY2 else str): pytest.skip(func) text = func(selector)