From 03b5d41cd9d9f194865c0e55e4a5794438714dc1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Wed, 5 Nov 2025 16:43:06 +0000 Subject: [PATCH 1/4] Implement noconvert for std::filesystem::path The current cast implementation for std::filesystem::path is to always cast. If the argument is marked as noconvert() it will convert regardless. This change rejects the argument if convert is false and the argument is not an instance of pathlib.Path. --- include/pybind11/stl/filesystem.h | 6 +++++- tests/test_stl.cpp | 3 +++ tests/test_stl.py | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index 52d2962107..ec4896688c 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -58,7 +58,11 @@ struct path_caster { return nullptr; } - bool load(handle handle, bool) { + bool load(handle handle, bool convert) { + if (!convert && !PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr())) { + return false; + } + // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy // issue #3168) so we do it ourselves instead. diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 6084d517df..cb4a84c83c 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -456,6 +456,9 @@ TEST_SUBMODULE(stl, m) { // test_fs_path m.attr("has_filesystem") = true; m.def("parent_path", [](const std::filesystem::path &path) { return path.parent_path(); }); + m.def("parent_path_noconvert", + [](const std::filesystem::path &path) { return path.parent_path(); }, + py::arg("arg0").noconvert()); m.def("parent_paths", [](const std::vector &paths) { std::vector result; result.reserve(paths.size()); diff --git a/tests/test_stl.py b/tests/test_stl.py index 4a57635e27..b2d8c16232 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -279,6 +279,16 @@ def __fspath__(self): assert m.parent_path(b"foo/bar") == Path("foo") assert m.parent_path(PseudoStrPath()) == Path("foo") assert m.parent_path(PseudoBytesPath()) == Path("foo") + # Single argument noconvert + assert m.parent_path_noconvert(Path("foo/bar")) == Path("foo") + with pytest.raises(TypeError): + m.parent_path_noconvert("foo/bar") + with pytest.raises(TypeError): + m.parent_path_noconvert(b"foo/bar") + with pytest.raises(TypeError): + m.parent_path_noconvert(PseudoStrPath()) + with pytest.raises(TypeError): + m.parent_path_noconvert(PseudoBytesPath()) # std::vector assert m.parent_paths(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")] # py::typing::List @@ -311,6 +321,11 @@ def test_path_typing(doc): doc(m.parent_path) == "parent_path(arg0: os.PathLike | str | bytes) -> pathlib.Path" ) + # Single argument noconvert + assert ( + doc(m.parent_path_noconvert) + == "parent_path_noconvert(arg0: pathlib.Path) -> pathlib.Path" + ) # std::vector assert ( doc(m.parent_paths) From ba17cca87dd9c1e9e2acfa72015ed66b0b78e90a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:45:18 +0000 Subject: [PATCH 2/4] style: pre-commit fixes --- include/pybind11/stl/filesystem.h | 5 +++-- tests/test_stl.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index ec4896688c..f51257d028 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -59,10 +59,11 @@ struct path_caster { } bool load(handle handle, bool convert) { - if (!convert && !PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr())) { + if (!convert + && !PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr())) { return false; } - + // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy // issue #3168) so we do it ourselves instead. diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index cb4a84c83c..55ec708522 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -456,7 +456,8 @@ TEST_SUBMODULE(stl, m) { // test_fs_path m.attr("has_filesystem") = true; m.def("parent_path", [](const std::filesystem::path &path) { return path.parent_path(); }); - m.def("parent_path_noconvert", + m.def( + "parent_path_noconvert", [](const std::filesystem::path &path) { return path.parent_path(); }, py::arg("arg0").noconvert()); m.def("parent_paths", [](const std::vector &paths) { From 08711f9f3e8a341fcac5e532ef4ebbee43ceca44 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Thu, 6 Nov 2025 09:12:33 +0000 Subject: [PATCH 3/4] Fix warning --- include/pybind11/stl/filesystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index f51257d028..a1d90285af 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -60,7 +60,7 @@ struct path_caster { bool load(handle handle, bool convert) { if (!convert - && !PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr())) { + && PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr()) != 1) { return false; } From 07c854f2f37c1cc2410b6002d17699d542470b77 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:13:01 +0000 Subject: [PATCH 4/4] style: pre-commit fixes --- include/pybind11/stl/filesystem.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index a1d90285af..56665b5c16 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -60,7 +60,8 @@ struct path_caster { bool load(handle handle, bool convert) { if (!convert - && PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr()) != 1) { + && PyObject_IsInstance(handle.ptr(), module_::import("pathlib").attr("Path").ptr()) + != 1) { return false; }