Files
pybind11/tests/test_docs_advanced_cast_custom.cpp
gentlegiantJGC 67424358f4 fix(types): add typing and collections.abc module prefix (#5663)
* Fix Python 3.8 type hints and add module prefix

These type hints are invalid in Python 3.8.
Add `typing.` prefix to remove ambiguity.

* style: pre-commit fixes

* Add module prefix to Union

* Rename macros

* Improve comment

* Comment out 3.8 type hint macros

Fixing this issue in Python 3.8 will require updating lots of tests. This can be added in a further pull request.

* Add Iterable module prefix

* Add module prefix to Iterator

* Add module prefix to Callable

* Re-add accidentally deleted brackets

* Add module prefix to Optional

* Add module prefix to Final

* Add module prefix to ClassVar

* Add module prefix to TypeGuard

* Add module prefix to TypeIs

* Add module prefix to NoReturn

* Add module prefix to Never

* Add module prefix to Literal

* Add module prefix to Callable

* Add module prefix to Sequence

* Add module prefix to Iterator

* style: pre-commit fixes

* Remove type hint macros

* style: pre-commit fixes

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-05-19 11:54:31 -04:00

70 lines
2.6 KiB
C++

// #########################################################################
// PLEASE UPDATE docs/advanced/cast/custom.rst IF ANY CHANGES ARE MADE HERE.
// #########################################################################
#include "pybind11_tests.h"
namespace user_space {
struct Point2D {
double x;
double y;
};
Point2D negate(const Point2D &point) { return Point2D{-point.x, -point.y}; }
} // namespace user_space
namespace pybind11 {
namespace detail {
template <>
struct type_caster<user_space::Point2D> {
// This macro inserts a lot of boilerplate code and sets the type hint.
// `io_name` is used to specify different type hints for arguments and return values.
// The signature of our negate function would then look like:
// `negate(collections.abc.Sequence[float]) -> tuple[float, float]`
PYBIND11_TYPE_CASTER(user_space::Point2D,
io_name("collections.abc.Sequence[float]", "tuple[float, float]"));
// C++ -> Python: convert `Point2D` to `tuple[float, float]`. The second and third arguments
// are used to indicate the return value policy and parent object (for
// return_value_policy::reference_internal) and are often ignored by custom casters.
// The return value should reflect the type hint specified by the second argument of `io_name`.
static handle
cast(const user_space::Point2D &number, return_value_policy /*policy*/, handle /*parent*/) {
return py::make_tuple(number.x, number.y).release();
}
// Python -> C++: convert a `PyObject` into a `Point2D` and return false upon failure. The
// second argument indicates whether implicit conversions should be allowed.
// The accepted types should reflect the type hint specified by the first argument of
// `io_name`.
bool load(handle src, bool /*convert*/) {
// Check if handle is a Sequence
if (!py::isinstance<py::sequence>(src)) {
return false;
}
auto seq = py::reinterpret_borrow<py::sequence>(src);
// Check if exactly two values are in the Sequence
if (seq.size() != 2) {
return false;
}
// Check if each element is either a float or an int
for (auto item : seq) {
if (!py::isinstance<py::float_>(item) && !py::isinstance<py::int_>(item)) {
return false;
}
}
value.x = seq[0].cast<double>();
value.y = seq[1].cast<double>();
return true;
}
};
} // namespace detail
} // namespace pybind11
// Bind the negate function
TEST_SUBMODULE(docs_advanced_cast_custom, m) { m.def("negate", user_space::negate); }