From 1bbb284ad1ba270be165c576be736bfddae094f2 Mon Sep 17 00:00:00 2001 From: PJ Reiniger Date: Wed, 31 Dec 2025 12:03:24 -0500 Subject: [PATCH] [py][wpiutil] Add type caster for WPI_String (#8500) This adds a type caster for `WPI_String` so that pybind11 can more easily auto-convert between that and strings. This helps remove the need to do things like [this](https://github.com/wpilibsuite/allwpilib/pull/8498/commits/f1d77244c3c78a4777798068c8a2b8dd4dc83339) in the opmodes fixup --- wpiutil/src/main/python/pyproject.toml | 4 +++ .../src/type_casters/wpi_string_type_caster.h | 34 +++++++++++++++++++ .../test/python/cpp/wpiutil_test/module.cpp | 33 ++++++++++++++++++ wpiutil/src/test/python/test_wpi_string.py | 15 ++++++++ 4 files changed, 86 insertions(+) create mode 100644 wpiutil/src/main/python/wpiutil/src/type_casters/wpi_string_type_caster.h create mode 100644 wpiutil/src/test/python/test_wpi_string.py diff --git a/wpiutil/src/main/python/pyproject.toml b/wpiutil/src/main/python/pyproject.toml index 9591848853..e90ce9c031 100644 --- a/wpiutil/src/main/python/pyproject.toml +++ b/wpiutil/src/main/python/pyproject.toml @@ -120,6 +120,10 @@ types = ["wpi::util::StringMap"] header = "wpi_ct_string_type_caster.h" types = ["wpi::util::ct_string"] +[[tool.semiwrap.export_type_casters.wpiutil-casters.headers]] +header = "wpi_string_type_caster.h" +types = ["WPI_String"] + [[tool.semiwrap.export_type_casters.wpiutil-casters.headers]] header = "wpystruct.h" types = ["WPyStruct"] diff --git a/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_string_type_caster.h b/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_string_type_caster.h new file mode 100644 index 0000000000..abb17adaf7 --- /dev/null +++ b/wpiutil/src/main/python/wpiutil/src/type_casters/wpi_string_type_caster.h @@ -0,0 +1,34 @@ +#include + +#include "wpi/util/string.h" + +namespace pybind11::detail { + +template <> +struct type_caster { +public: + PYBIND11_TYPE_CASTER(WPI_String, _("str")); + + bool load(handle src, bool convert) { + if (!src) { + return false; + } + + Py_ssize_t size = -1; + const char *str = PyUnicode_AsUTF8AndSize(src.ptr(), &size); + if (!str) { + PyErr_Clear(); + return false; + } + + value = WPI_String(str, static_cast(size)); + + return true; + } + + static handle cast(const WPI_String& str, return_value_policy /* policy */, handle /* parent */) { + return PyUnicode_FromStringAndSize(str.str, str.len); + } +}; + +} // namespace pybind11::detail \ No newline at end of file diff --git a/wpiutil/src/test/python/cpp/wpiutil_test/module.cpp b/wpiutil/src/test/python/cpp/wpiutil_test/module.cpp index 8eb61211ea..2cef396bc3 100644 --- a/wpiutil/src/test/python/cpp/wpiutil_test/module.cpp +++ b/wpiutil/src/test/python/cpp/wpiutil_test/module.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -152,6 +153,29 @@ constexpr auto const_string() { void sendable_test(py::module &m); void struct_test(py::module &m); +/* WPI_String tests */ +struct StructWithWPI_String { + int x; + WPI_String str; +}; + +WPI_String load_wpi_string(WPI_String str) { + return str; +} + +WPI_String cast_wpi_string() { + WPI_String str = wpi::util::make_string("Hello WPI_String!"); + return str; +} + +StructWithWPI_String cast_struct_with_wpi_string() { + StructWithWPI_String output{ + .x = 3504, + .str = wpi::util::make_string("I'm in a struct!") + }; + + return output; +} PYBIND11_MODULE(module, m) { @@ -192,4 +216,13 @@ PYBIND11_MODULE(module, m) { m.attr("min_int64") = std::numeric_limits::min(); // ct_string m.def("const_string", &const_string); + + // WPI_String + m.def("load_wpi_string", &load_wpi_string); + m.def("cast_wpi_string", &cast_wpi_string); + + py::class_ structWithWpiStringCls(m, "StructWithWPI_String"); + structWithWpiStringCls.def_readwrite("x", &StructWithWPI_String::x); + structWithWpiStringCls.def_readonly("str", &StructWithWPI_String::str); + m.def("cast_struct_with_wpi_string", &cast_struct_with_wpi_string); }; diff --git a/wpiutil/src/test/python/test_wpi_string.py b/wpiutil/src/test/python/test_wpi_string.py new file mode 100644 index 0000000000..719252835d --- /dev/null +++ b/wpiutil/src/test/python/test_wpi_string.py @@ -0,0 +1,15 @@ +from wpiutil_test import module + + +def test_wpi_string_load(): + assert "Hello World" == module.load_wpi_string("Hello World") + + +def test_wpi_string_cast(): + assert "Hello WPI_String!" == module.cast_wpi_string() + + +def test_wpi_string_in_struct_cast(): + the_struct = module.cast_struct_with_wpi_string() + assert 3504 == the_struct.x + assert "I'm in a struct!" == the_struct.str