diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2c7d1708..6c4b3695 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,4 +4,8 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7973e812..9b1137f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -801,7 +801,7 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.14 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.12.1 + uses: ilammy/msvc-dev-cmd@v1.13.0 with: arch: x86 @@ -854,7 +854,7 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.14 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.12.1 + uses: ilammy/msvc-dev-cmd@v1.13.0 with: arch: x86 @@ -960,7 +960,6 @@ jobs: mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-python-pip mingw-w64-${{matrix.env}}-python-numpy - mingw-w64-${{matrix.env}}-python-scipy mingw-w64-${{matrix.env}}-cmake mingw-w64-${{matrix.env}}-make mingw-w64-${{matrix.env}}-python-pytest @@ -968,6 +967,14 @@ jobs: mingw-w64-${{matrix.env}}-boost mingw-w64-${{matrix.env}}-catch + - uses: msys2/setup-msys2@v2 + if: matrix.sys == 'mingw64' + with: + msystem: ${{matrix.sys}} + install: >- + git + mingw-w64-${{matrix.env}}-python-scipy + - uses: actions/checkout@v4 - name: Configure C++11 diff --git a/.github/workflows/ci_sh_def.yml b/.github/workflows/ci_sh_def.yml index 1fa7ddef..277a8ea6 100644 --- a/.github/workflows/ci_sh_def.yml +++ b/.github/workflows/ci_sh_def.yml @@ -824,7 +824,7 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.14 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.12.1 + uses: ilammy/msvc-dev-cmd@v1.13.0 with: arch: x86 @@ -878,7 +878,7 @@ jobs: uses: jwlawson/actions-setup-cmake@v1.14 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1.12.1 + uses: ilammy/msvc-dev-cmd@v1.13.0 with: arch: x86 @@ -987,7 +987,6 @@ jobs: mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-python-pip mingw-w64-${{matrix.env}}-python-numpy - mingw-w64-${{matrix.env}}-python-scipy mingw-w64-${{matrix.env}}-cmake mingw-w64-${{matrix.env}}-make mingw-w64-${{matrix.env}}-python-pytest @@ -995,6 +994,14 @@ jobs: mingw-w64-${{matrix.env}}-boost mingw-w64-${{matrix.env}}-catch + - uses: msys2/setup-msys2@v2 + if: matrix.sys == 'mingw64' + with: + msystem: ${{matrix.sys}} + install: >- + git + mingw-w64-${{matrix.env}}-python-scipy + - uses: actions/checkout@v4 - name: Configure C++11 diff --git a/.github/workflows/ci_sh_def.yml.patch b/.github/workflows/ci_sh_def.yml.patch index fc4378eb..a681f831 100644 --- a/.github/workflows/ci_sh_def.yml.patch +++ b/.github/workflows/ci_sh_def.yml.patch @@ -1,5 +1,5 @@ ---- ci.yml 2023-12-14 23:14:57.621249678 -0800 -+++ ci_sh_def.yml 2023-12-14 23:58:28.144667703 -0800 +--- ci.yml 2024-01-16 21:10:28.100295655 -0800 ++++ ci_sh_def.yml 2024-01-16 21:10:56.792251785 -0800 @@ -1,4 +1,16 @@ -name: CI +# PLEASE KEEP THIS GROUP OF FILES IN SYNC AT ALL TIMES: @@ -165,7 +165,7 @@ "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" - name: Build C++20 - Exercise cmake -DPYBIND11_TEST_OVERRIDE -@@ -976,6 +1003,7 @@ +@@ -983,6 +1010,7 @@ run: >- cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") @@ -173,7 +173,7 @@ -S . -B build - name: Build C++11 -@@ -997,6 +1025,7 @@ +@@ -1004,6 +1032,7 @@ run: >- cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") @@ -181,7 +181,7 @@ -S . -B build2 - name: Build C++14 -@@ -1018,6 +1047,7 @@ +@@ -1025,6 +1054,7 @@ run: >- cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(python -c "import sys; print(sys.executable)") @@ -189,7 +189,7 @@ -S . -B build3 - name: Build C++17 -@@ -1085,6 +1115,7 @@ +@@ -1092,6 +1122,7 @@ -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17 @@ -197,7 +197,7 @@ - name: Build run: cmake --build . -j 2 -@@ -1150,6 +1181,7 @@ +@@ -1157,6 +1188,7 @@ -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17 @@ -205,7 +205,7 @@ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") - name: Build -@@ -1173,6 +1205,7 @@ +@@ -1180,6 +1212,7 @@ -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b09fdfd2..5db12cc9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: # Ruff, the Python auto-correcting linter/formatter written in Rust - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.9 hooks: - id: ruff args: ["--fix", "--show-fixes"] @@ -40,7 +40,7 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.7.1" + rev: "v1.8.0" hooks: - id: mypy args: [] @@ -144,7 +144,7 @@ repos: # PyLint has native support - not always usable, but works for us - repo: https://github.com/PyCQA/pylint - rev: "v3.0.1" + rev: "v3.0.3" hooks: - id: pylint files: ^pybind11 diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 6212063b..168c1b23 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -313,7 +313,7 @@ struct list_caster { auto s = reinterpret_borrow(seq); value.clear(); reserve_maybe(s, &value); - for (auto it : seq) { + for (const auto &it : seq) { value_conv conv; if (!conv.load(it, convert)) { return false; @@ -383,7 +383,7 @@ struct array_caster { // https://en.cppreference.com/w/cpp/named_req/NumericType value->resize(l.size()); size_t ctr = 0; - for (auto it : l) { + for (const auto &it : l) { value_conv conv; if (!conv.load(it, convert)) { return false; diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index bc8dcfc6..3d24bc5a 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -645,49 +645,50 @@ auto map_if_insertion_operator(Class_ &cl, std::string const &name) "Return the canonical string representation of this map."); } -template struct keys_view { virtual size_t len() = 0; virtual iterator iter() = 0; - virtual bool contains(const KeyType &k) = 0; - virtual bool contains(const object &k) = 0; + virtual bool contains(const handle &k) = 0; virtual ~keys_view() = default; }; -template struct values_view { virtual size_t len() = 0; virtual iterator iter() = 0; virtual ~values_view() = default; }; -template struct items_view { virtual size_t len() = 0; virtual iterator iter() = 0; virtual ~items_view() = default; }; -template -struct KeysViewImpl : public KeysView { +template +struct KeysViewImpl : public detail::keys_view { explicit KeysViewImpl(Map &map) : map(map) {} size_t len() override { return map.size(); } iterator iter() override { return make_key_iterator(map.begin(), map.end()); } - bool contains(const typename Map::key_type &k) override { return map.find(k) != map.end(); } - bool contains(const object &) override { return false; } + bool contains(const handle &k) override { + try { + return map.find(k.template cast()) != map.end(); + } catch (const cast_error &) { + return false; + } + } Map ↦ }; -template -struct ValuesViewImpl : public ValuesView { +template +struct ValuesViewImpl : public detail::values_view { explicit ValuesViewImpl(Map &map) : map(map) {} size_t len() override { return map.size(); } iterator iter() override { return make_value_iterator(map.begin(), map.end()); } Map ↦ }; -template -struct ItemsViewImpl : public ItemsView { +template +struct ItemsViewImpl : public detail::items_view { explicit ItemsViewImpl(Map &map) : map(map) {} size_t len() override { return map.size(); } iterator iter() override { return make_iterator(map.begin(), map.end()); } @@ -700,11 +701,9 @@ template , typenam class_ bind_map(handle scope, const std::string &name, Args &&...args) { using KeyType = typename Map::key_type; using MappedType = typename Map::mapped_type; - using StrippedKeyType = detail::remove_cvref_t; - using StrippedMappedType = detail::remove_cvref_t; - using KeysView = detail::keys_view; - using ValuesView = detail::values_view; - using ItemsView = detail::items_view; + using KeysView = detail::keys_view; + using ValuesView = detail::values_view; + using ItemsView = detail::items_view; using Class_ = class_; // If either type is a non-module-local bound type then make the map binding non-local as well; @@ -718,39 +717,20 @@ class_ bind_map(handle scope, const std::string &name, Args && } Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward(args)...); - static constexpr auto key_type_descr = detail::make_caster::name; - static constexpr auto mapped_type_descr = detail::make_caster::name; - std::string key_type_name(key_type_descr.text), mapped_type_name(mapped_type_descr.text); - // If key type isn't properly wrapped, fall back to C++ names - if (key_type_name == "%") { - key_type_name = detail::type_info_description(typeid(KeyType)); - } - // Similarly for value type: - if (mapped_type_name == "%") { - mapped_type_name = detail::type_info_description(typeid(MappedType)); - } - - // Wrap KeysView[KeyType] if it wasn't already wrapped + // Wrap KeysView if it wasn't already wrapped if (!detail::get_type_info(typeid(KeysView))) { - class_ keys_view( - scope, ("KeysView[" + key_type_name + "]").c_str(), pybind11::module_local(local)); + class_ keys_view(scope, "KeysView", pybind11::module_local(local)); keys_view.def("__len__", &KeysView::len); keys_view.def("__iter__", &KeysView::iter, keep_alive<0, 1>() /* Essential: keep view alive while iterator exists */ ); - keys_view.def("__contains__", - static_cast(&KeysView::contains)); - // Fallback for when the object is not of the key type - keys_view.def("__contains__", - static_cast(&KeysView::contains)); + keys_view.def("__contains__", &KeysView::contains); } // Similarly for ValuesView: if (!detail::get_type_info(typeid(ValuesView))) { - class_ values_view(scope, - ("ValuesView[" + mapped_type_name + "]").c_str(), - pybind11::module_local(local)); + class_ values_view(scope, "ValuesView", pybind11::module_local(local)); values_view.def("__len__", &ValuesView::len); values_view.def("__iter__", &ValuesView::iter, @@ -759,10 +739,7 @@ class_ bind_map(handle scope, const std::string &name, Args && } // Similarly for ItemsView: if (!detail::get_type_info(typeid(ItemsView))) { - class_ items_view( - scope, - ("ItemsView[" + key_type_name + ", ").append(mapped_type_name + "]").c_str(), - pybind11::module_local(local)); + class_ items_view(scope, "ItemsView", pybind11::module_local(local)); items_view.def("__len__", &ItemsView::len); items_view.def("__iter__", &ItemsView::iter, @@ -788,25 +765,19 @@ class_ bind_map(handle scope, const std::string &name, Args && cl.def( "keys", - [](Map &m) { - return std::unique_ptr(new detail::KeysViewImpl(m)); - }, + [](Map &m) { return std::unique_ptr(new detail::KeysViewImpl(m)); }, keep_alive<0, 1>() /* Essential: keep map alive while view exists */ ); cl.def( "values", - [](Map &m) { - return std::unique_ptr(new detail::ValuesViewImpl(m)); - }, + [](Map &m) { return std::unique_ptr(new detail::ValuesViewImpl(m)); }, keep_alive<0, 1>() /* Essential: keep map alive while view exists */ ); cl.def( "items", - [](Map &m) { - return std::unique_ptr(new detail::ItemsViewImpl(m)); - }, + [](Map &m) { return std::unique_ptr(new detail::ItemsViewImpl(m)); }, keep_alive<0, 1>() /* Essential: keep map alive while view exists */ ); diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index 4271b421..1d1d8a16 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -103,21 +103,26 @@ class MyObject3 : public std::enable_shared_from_this { int value; }; +template +std::unordered_set &pointer_set() { + // https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables + static auto singleton = new std::unordered_set(); + return *singleton; +} + // test_unique_nodelete // Object with a private destructor -class MyObject4; -std::unordered_set myobject4_instances; class MyObject4 { public: explicit MyObject4(int value) : value{value} { print_created(this); - myobject4_instances.insert(this); + pointer_set().insert(this); } int value; static void cleanupAllInstances() { - auto tmp = std::move(myobject4_instances); - myobject4_instances.clear(); + auto tmp = std::move(pointer_set()); + pointer_set().clear(); for (auto *o : tmp) { delete o; } @@ -125,7 +130,7 @@ class MyObject4 { private: ~MyObject4() { - myobject4_instances.erase(this); + pointer_set().erase(this); print_destroyed(this); } }; @@ -133,19 +138,17 @@ class MyObject4 { // test_unique_deleter // Object with std::unique_ptr where D is not matching the base class // Object with a protected destructor -class MyObject4a; -std::unordered_set myobject4a_instances; class MyObject4a { public: explicit MyObject4a(int i) : value{i} { print_created(this); - myobject4a_instances.insert(this); + pointer_set().insert(this); }; int value; static void cleanupAllInstances() { - auto tmp = std::move(myobject4a_instances); - myobject4a_instances.clear(); + auto tmp = std::move(pointer_set()); + pointer_set().clear(); for (auto *o : tmp) { delete o; } @@ -153,7 +156,7 @@ class MyObject4a { protected: virtual ~MyObject4a() { - myobject4a_instances.erase(this); + pointer_set().erase(this); print_destroyed(this); } }; diff --git a/tests/test_stl_binders.cpp b/tests/test_stl_binders.cpp index e52a03b6..96af9a4c 100644 --- a/tests/test_stl_binders.cpp +++ b/tests/test_stl_binders.cpp @@ -192,6 +192,16 @@ TEST_SUBMODULE(stl_binders, m) { py::bind_map>(m, "UnorderedMapStringDoubleConst"); + // test_map_view_types + py::bind_map>(m, "MapStringFloat"); + py::bind_map>(m, "UnorderedMapStringFloat"); + + py::bind_map, int32_t>>(m, "MapPairDoubleIntInt32"); + py::bind_map, int64_t>>(m, "MapPairDoubleIntInt64"); + + py::bind_map>(m, "MapIntObject"); + py::bind_map>(m, "MapStringObject"); + py::class_(m, "ENC").def(py::init()).def_readwrite("value", &E_nc::value); // test_noncopyable_containers diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index c00d45b9..d1bf64aa 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -317,9 +317,9 @@ def test_map_view_types(): map_string_double_const = m.MapStringDoubleConst() unordered_map_string_double_const = m.UnorderedMapStringDoubleConst() - assert map_string_double.keys().__class__.__name__ == "KeysView[str]" - assert map_string_double.values().__class__.__name__ == "ValuesView[float]" - assert map_string_double.items().__class__.__name__ == "ItemsView[str, float]" + assert map_string_double.keys().__class__.__name__ == "KeysView" + assert map_string_double.values().__class__.__name__ == "ValuesView" + assert map_string_double.items().__class__.__name__ == "ItemsView" keys_type = type(map_string_double.keys()) assert type(unordered_map_string_double.keys()) is keys_type @@ -336,6 +336,30 @@ def test_map_view_types(): assert type(map_string_double_const.items()) is items_type assert type(unordered_map_string_double_const.items()) is items_type + map_string_float = m.MapStringFloat() + unordered_map_string_float = m.UnorderedMapStringFloat() + + assert type(map_string_float.keys()) is keys_type + assert type(unordered_map_string_float.keys()) is keys_type + assert type(map_string_float.values()) is values_type + assert type(unordered_map_string_float.values()) is values_type + assert type(map_string_float.items()) is items_type + assert type(unordered_map_string_float.items()) is items_type + + map_pair_double_int_int32 = m.MapPairDoubleIntInt32() + map_pair_double_int_int64 = m.MapPairDoubleIntInt64() + + assert type(map_pair_double_int_int32.values()) is values_type + assert type(map_pair_double_int_int64.values()) is values_type + + map_int_object = m.MapIntObject() + map_string_object = m.MapStringObject() + + assert type(map_int_object.keys()) is keys_type + assert type(map_string_object.keys()) is keys_type + assert type(map_int_object.items()) is items_type + assert type(map_string_object.items()) is items_type + def test_recursive_vector(): recursive_vector = m.RecursiveVector() diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index cd88a645..9fe2eb08 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -110,15 +110,17 @@ if(NOT DEFINED ${_Python}_EXECUTABLE) endif() -if(NOT ${_Python}_EXECUTABLE STREQUAL PYBIND11_PYTHON_EXECUTABLE_LAST) +if(DEFINED PYBIND11_PYTHON_EXECUTABLE_LAST AND NOT ${_Python}_EXECUTABLE STREQUAL + PYBIND11_PYTHON_EXECUTABLE_LAST) # Detect changes to the Python version/binary in subsequent CMake runs, and refresh config if needed unset(PYTHON_IS_DEBUG CACHE) unset(PYTHON_MODULE_EXTENSION CACHE) - set(PYBIND11_PYTHON_EXECUTABLE_LAST - "${${_Python}_EXECUTABLE}" - CACHE INTERNAL "Python executable during the last CMake run") endif() +set(PYBIND11_PYTHON_EXECUTABLE_LAST + "${${_Python}_EXECUTABLE}" + CACHE INTERNAL "Python executable during the last CMake run") + if(NOT DEFINED PYTHON_IS_DEBUG) # Debug check - see https://stackoverflow.com/questions/646518/python-how-to-detect-debug-Interpreter execute_process(