From dd829c59946d10e1c9b2b3e0e74edee289b44c20 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Fri, 6 Sep 2024 14:25:42 +0200 Subject: [PATCH 1/6] Added option to add `return_name` to custom `type_caster`. This allows to define different python type hints for arguments and return value. This is implemented using template class `as_return_type` in order to check if `return_name` is available in the `type_caster` at compile time. If `return_name` is not available, it falls back to the previous usage of `name` for both args and return value. --- include/pybind11/pybind11.h | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 1806583e6..d71068bae 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -101,6 +101,31 @@ inline std::string replace_newlines_and_squash(const char *text) { # define PYBIND11_COMPAT_STRDUP strdup #endif +/// Type trait checker for `descr`. +template +struct is_descr : std::false_type {}; + +template +struct is_descr> : std::true_type {}; + +template +struct is_descr> : std::true_type {}; + +/// Checks for `return_name` in `type_caster` to replace `name` for return type hints. +/// this is useful for having a different python type hint for args vs return value, +/// e.g., `std::filesystem::path` -> Arg: `Union[os.PathLike, str, bytes]`, return: `Path`. +template +struct as_return_type +{ + static constexpr auto name = T::name; +}; + +template +struct as_return_type::value>> +{ + static constexpr auto name = T::return_name; +}; + PYBIND11_NAMESPACE_END(detail) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object @@ -320,7 +345,8 @@ protected: /* Generate a readable signature describing the function's arguments and return value types */ static constexpr auto signature - = const_name("(") + cast_in::arg_names + const_name(") -> ") + cast_out::name; + = const_name("(") + cast_in::arg_names + const_name(") -> ") + + as_return_type::name; PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); /* Register the function with Python from generic (non-templated) code */ From 7e7f9ce67b299882b409482b55e4699da120aa30 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Fri, 6 Sep 2024 14:30:41 +0200 Subject: [PATCH 2/6] Applied `return_name` to `type_caster` for `std::fileystem::path`. The `type_caster` previously named `os.PathLike` as both argument and return type. This is inaccurate since it also takes `str` and `byte` as argument and actually returns `Path`. This commit uses the new `return_name` feature to define argument type as `Union[os.PathLike, str, bytes]` and return type as `Path`. An assert was added to the unit test for this. --- include/pybind11/stl/filesystem.h | 3 ++- tests/test_stl.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/pybind11/stl/filesystem.h b/include/pybind11/stl/filesystem.h index c16a9ae5c..a06248e38 100644 --- a/include/pybind11/stl/filesystem.h +++ b/include/pybind11/stl/filesystem.h @@ -106,7 +106,8 @@ public: return true; } - PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); + PYBIND11_TYPE_CASTER(T, const_name("Union[os.PathLike, str, bytes]")); + static constexpr auto return_name = const_name("Path"); }; #endif // PYBIND11_HAS_FILESYSTEM || defined(PYBIND11_HAS_EXPERIMENTAL_FILESYSTEM) diff --git a/tests/test_stl.py b/tests/test_stl.py index 6f548789e..bf332c4a3 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -245,7 +245,7 @@ def test_reference_sensitive_optional(): @pytest.mark.skipif(not hasattr(m, "has_filesystem"), reason="no ") -def test_fs_path(): +def test_fs_path(doc): from pathlib import Path class PseudoStrPath: @@ -261,6 +261,7 @@ def test_fs_path(): assert m.parent_path(b"foo/bar") == Path("foo") assert m.parent_path(PseudoStrPath()) == Path("foo") assert m.parent_path(PseudoBytesPath()) == Path("foo") + assert doc(m.parent_path) == "parent_path(arg0: Union[os.PathLike, str, bytes]) -> Path" @pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no ") From e8aa81bd9a909929271ec09f952fc06b075fe407 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Fri, 6 Sep 2024 15:24:25 +0200 Subject: [PATCH 3/6] Applied pre-commit formatting. --- include/pybind11/pybind11.h | 19 ++++++++----------- tests/test_stl.py | 5 ++++- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index d71068bae..79f029090 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -102,27 +102,25 @@ inline std::string replace_newlines_and_squash(const char *text) { #endif /// Type trait checker for `descr`. -template +template struct is_descr : std::false_type {}; -template +template struct is_descr> : std::true_type {}; -template +template struct is_descr> : std::true_type {}; /// Checks for `return_name` in `type_caster` to replace `name` for return type hints. /// this is useful for having a different python type hint for args vs return value, /// e.g., `std::filesystem::path` -> Arg: `Union[os.PathLike, str, bytes]`, return: `Path`. -template -struct as_return_type -{ +template +struct as_return_type { static constexpr auto name = T::name; }; template -struct as_return_type::value>> -{ +struct as_return_type::value>> { static constexpr auto name = T::return_name; }; @@ -344,9 +342,8 @@ protected: /* Generate a readable signature describing the function's arguments and return value types */ - static constexpr auto signature - = const_name("(") + cast_in::arg_names + const_name(") -> ") - + as_return_type::name; + static constexpr auto signature = const_name("(") + cast_in::arg_names + + const_name(") -> ") + as_return_type::name; PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types(); /* Register the function with Python from generic (non-templated) code */ diff --git a/tests/test_stl.py b/tests/test_stl.py index bf332c4a3..d2d89420f 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -261,7 +261,10 @@ def test_fs_path(doc): assert m.parent_path(b"foo/bar") == Path("foo") assert m.parent_path(PseudoStrPath()) == Path("foo") assert m.parent_path(PseudoBytesPath()) == Path("foo") - assert doc(m.parent_path) == "parent_path(arg0: Union[os.PathLike, str, bytes]) -> Path" + assert ( + doc(m.parent_path) + == "parent_path(arg0: Union[os.PathLike, str, bytes]) -> Path" + ) @pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no ") From 85052afe6562671f13c4fc8b23f8383de60fd772 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Fri, 6 Sep 2024 15:50:12 +0200 Subject: [PATCH 4/6] Using `std::enable_if` instead of `std::enable_if_t` for C++ support. --- include/pybind11/pybind11.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 79f029090..561bf6043 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -120,7 +120,7 @@ struct as_return_type { }; template -struct as_return_type::value>> { +struct as_return_type::value>::type> { static constexpr auto name = T::return_name; }; From 23ceaba9742c3c166fd13095d0b9f255e3ffe5a7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:55:24 +0000 Subject: [PATCH 5/6] style: pre-commit fixes --- include/pybind11/pybind11.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 561bf6043..582481689 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -120,7 +120,8 @@ struct as_return_type { }; template -struct as_return_type::value>::type> { +struct as_return_type::value>::type> { static constexpr auto name = T::return_name; }; From 7d16bad5a94315d02d74d5333d32d395d9d52267 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Fri, 6 Sep 2024 18:06:54 +0200 Subject: [PATCH 6/6] Added unit test for list/vector of paths. --- tests/test_stl.cpp | 7 +++++++ tests/test_stl.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index dd93d51d0..08456b3c0 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -454,6 +454,13 @@ TEST_SUBMODULE(stl, m) { // test_fs_path m.attr("has_filesystem") = true; m.def("parent_path", [](const std::filesystem::path &p) { return p.parent_path(); }); + m.def("parent_paths", [](const std::vector &p) { + std::vector result; + for (const auto &i : p) { + result.push_back(i.parent_path()); + } + return result; + }); #endif #ifdef PYBIND11_TEST_VARIANT diff --git a/tests/test_stl.py b/tests/test_stl.py index d2d89420f..2c3172703 100644 --- a/tests/test_stl.py +++ b/tests/test_stl.py @@ -265,6 +265,11 @@ def test_fs_path(doc): doc(m.parent_path) == "parent_path(arg0: Union[os.PathLike, str, bytes]) -> Path" ) + assert m.parent_paths(["foo/bar", "foo/baz"]) == [Path("foo"), Path("foo")] + assert ( + doc(m.parent_paths) + == "parent_paths(arg0: list[Union[os.PathLike, str, bytes]]) -> list[Path]" + ) @pytest.mark.skipif(not hasattr(m, "load_variant"), reason="no ")