From 2be85c604167e4097edab9cd7b4215e85f1ae8d1 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 25 Jun 2024 22:53:15 -0400 Subject: [PATCH] feat(types): adds support for TypeGuard and TypeIs (#5194) * Adds support for TypeGuard and TypeIs * style: pre-commit fixes --------- Co-authored-by: Henry Schreiner Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/pybind11/typing.h | 22 ++++++++++++++++++++++ tests/test_pytypes.cpp | 7 +++++++ tests/test_pytypes.py | 11 +++++++++++ 3 files changed, 40 insertions(+) diff --git a/include/pybind11/typing.h b/include/pybind11/typing.h index 55f1f949d..cf70b739e 100644 --- a/include/pybind11/typing.h +++ b/include/pybind11/typing.h @@ -80,6 +80,16 @@ class Optional : public object { using object::object; }; +template +class TypeGuard : public bool_ { + using bool_::bool_; +}; + +template +class TypeIs : public bool_ { + using bool_::bool_; +}; + class NoReturn : public none { using none::none; }; @@ -87,6 +97,7 @@ class NoReturn : public none { class Never : public none { using none::none; }; + #if defined(__cpp_nontype_template_parameter_class) template struct StringLiteral { @@ -183,6 +194,16 @@ struct handle_type_name> { static constexpr auto name = const_name("Optional[") + make_caster::name + const_name("]"); }; +template +struct handle_type_name> { + static constexpr auto name = const_name("TypeGuard[") + make_caster::name + const_name("]"); +}; + +template +struct handle_type_name> { + static constexpr auto name = const_name("TypeIs[") + make_caster::name + const_name("]"); +}; + template <> struct handle_type_name { static constexpr auto name = const_name("NoReturn"); @@ -192,6 +213,7 @@ template <> struct handle_type_name { static constexpr auto name = const_name("Never"); }; + #if defined(__cpp_nontype_template_parameter_class) template struct handle_type_name> { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index ce6b93326..6b347894b 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -892,8 +892,15 @@ TEST_SUBMODULE(pytypes, m) { return list; }); + m.def("annotate_type_guard", [](py::object &o) -> py::typing::TypeGuard { + return py::isinstance(o); + }); + m.def("annotate_type_is", + [](py::object &o) -> py::typing::TypeIs { return py::isinstance(o); }); + m.def("annotate_no_return", []() -> py::typing::NoReturn { throw 0; }); m.def("annotate_never", []() -> py::typing::Never { throw 0; }); + m.def("annotate_optional_to_object", [](py::typing::Optional &o) -> py::object { return o; }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 923cebaf5..92a3a36bf 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -991,6 +991,17 @@ def test_optional_annotations(doc): ) +def test_type_guard_annotations(doc): + assert ( + doc(m.annotate_type_guard) + == "annotate_type_guard(arg0: object) -> TypeGuard[str]" + ) + + +def test_type_is_annotations(doc): + assert doc(m.annotate_type_is) == "annotate_type_is(arg0: object) -> TypeIs[str]" + + def test_no_return_annotation(doc): assert doc(m.annotate_no_return) == "annotate_no_return() -> NoReturn"