Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(types): add TypeVars / method generics typing #5167

Merged
merged 54 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d872367
typevar prototype
InvincibleRMC Jun 14, 2024
1012f10
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
fbd168a
change to NameT
InvincibleRMC Jun 14, 2024
34bf41b
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
63acf62
make string const
InvincibleRMC Jun 14, 2024
38fd357
add missing closing bracket
InvincibleRMC Jun 14, 2024
614d03e
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
d354512
clean up handle_type_name
InvincibleRMC Jun 14, 2024
7b8c860
Merge branch 'typevars' of github.com:InvincibleRMC/pybind11 into typ…
InvincibleRMC Jun 14, 2024
a198152
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
ee00f70
add back missing <
InvincibleRMC Jun 14, 2024
e19ee22
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
98f836e
add back NameT
InvincibleRMC Jun 14, 2024
e1dc7a5
try fixed_string
InvincibleRMC Jun 14, 2024
14ea23d
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
5e5066c
std::basic_fixed_string
InvincibleRMC Jun 14, 2024
534dd65
test c++20
InvincibleRMC Jun 14, 2024
651227f
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
0f8864c
cleanup
InvincibleRMC Jun 14, 2024
b549902
fix object to typevar conversion
InvincibleRMC Jun 14, 2024
a858638
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
0380dc1
And CPP20 checks
InvincibleRMC Jun 14, 2024
3cc5a14
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
34ecc43
add missing cpp20++ check
InvincibleRMC Jun 14, 2024
dd8d648
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
e98b6a0
Add C++20 check to python
InvincibleRMC Jun 14, 2024
4122505
Fix python if {
InvincibleRMC Jun 14, 2024
c66ebd7
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
9954c18
update test name
InvincibleRMC Jun 14, 2024
7e07f5e
Merge
InvincibleRMC Jun 14, 2024
bd954e6
style: pre-commit fixes
pre-commit-ci[bot] Jun 14, 2024
8675c5c
remove call on cpp_std
InvincibleRMC Jun 14, 2024
9cd1b16
make field const
InvincibleRMC Jun 14, 2024
4e3821d
test nontype_template
InvincibleRMC Jun 14, 2024
dd3d0cc
update feature check
InvincibleRMC Jun 14, 2024
0a34ddb
update name of guard
InvincibleRMC Jun 14, 2024
c572871
fix try except in test
InvincibleRMC Jun 14, 2024
676e3bd
fix pre commit
InvincibleRMC Jun 14, 2024
5dd1d3b
remove extra semi colon
InvincibleRMC Jun 14, 2024
98c115d
except AttributeError
InvincibleRMC Jun 14, 2024
1dab40f
fix try except in test
InvincibleRMC Jun 14, 2024
39516fd
remove const
InvincibleRMC Jun 14, 2024
237136a
Clean up tests
InvincibleRMC Jun 15, 2024
d13e3d5
style: pre-commit fixes
pre-commit-ci[bot] Jun 15, 2024
a6c5676
use contextlib.suppres
InvincibleRMC Jun 15, 2024
2542b79
Merge remote-tracking branch 'origin/master' into typevars
InvincibleRMC Jun 15, 2024
ca868f8
request changes
InvincibleRMC Jun 15, 2024
0789290
Merge branch 'master' into typevars
InvincibleRMC Jun 15, 2024
32fde8e
lint
InvincibleRMC Jun 15, 2024
318ec6d
Add comments
InvincibleRMC Jun 15, 2024
e9f7889
style: pre-commit fixes
pre-commit-ci[bot] Jun 15, 2024
648c5a0
Add support for unions and optionals to be compatible with object
InvincibleRMC Jun 15, 2024
c452d2f
lint
InvincibleRMC Jun 15, 2024
7b83557
remove comment
InvincibleRMC Jun 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions include/pybind11/typing.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,32 @@ class Type : public type {

template <typename... Types>
class Union : public object {
PYBIND11_OBJECT_DEFAULT(Union, object, PyObject_Type)
using object::object;
};

template <typename T>
class Optional : public object {
PYBIND11_OBJECT_DEFAULT(Optional, object, PyObject_Type)
using object::object;
};

#if defined(__cpp_nontype_template_parameter_class)
template <size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
char value[N];
rwgk marked this conversation as resolved.
Show resolved Hide resolved
};

// Example syntax for creating a TypeVar.
// typedef typing::TypeVar<"T"> TypeVarT;
template <StringLiteral>
class TypeVar : public object {
PYBIND11_OBJECT_DEFAULT(TypeVar, object, PyObject_Type)
using object::object;
};
#endif

PYBIND11_NAMESPACE_END(typing)

PYBIND11_NAMESPACE_BEGIN(detail)
Expand Down Expand Up @@ -153,5 +171,12 @@ struct handle_type_name<typing::Optional<T>> {
static constexpr auto name = const_name("Optional[") + make_caster<T>::name + const_name("]");
};

#if defined(__cpp_nontype_template_parameter_class)
template <typing::StringLiteral StrLit>
struct handle_type_name<typing::TypeVar<StrLit>> {
static constexpr auto name = const_name(StrLit.value);
};
#endif

PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
28 changes: 27 additions & 1 deletion tests/test_pytypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ void m_defs(py::module_ &m) {

} // namespace handle_from_move_only_type_with_operator_PyObject

#if defined(__cpp_nontype_template_parameter_class)
namespace typevar {
typedef py::typing::TypeVar<"T"> TypeVarT;
typedef py::typing::TypeVar<"V"> TypeVarV;
} // namespace typevar
#endif

TEST_SUBMODULE(pytypes, m) {
m.def("obj_class_name", [](py::handle obj) { return py::detail::obj_class_name(obj.ptr()); });

Expand Down Expand Up @@ -844,7 +851,7 @@ TEST_SUBMODULE(pytypes, m) {
m.def("annotate_iterator_int", [](const py::typing::Iterator<int> &) {});
m.def("annotate_fn",
[](const py::typing::Callable<int(py::typing::List<py::str>, py::str)> &) {});
m.def("annotate_type", [](const py::typing::Type<int> &) {});
m.def("annotate_type", [](const py::typing::Type<int> &t) -> py::type { return t; });

m.def("annotate_union",
[](py::typing::List<py::typing::Union<py::str, py::int_, py::object>> l,
Expand All @@ -861,10 +868,29 @@ TEST_SUBMODULE(pytypes, m) {
[](py::typing::List<py::typing::Union<py::str>> &l)
-> py::typing::List<py::typing::Union<py::int_>> { return l; });

m.def("annotate_union_to_object",
[](py::typing::Union<int, py::str> &o) -> py::object { return o; });

m.def("annotate_optional",
[](py::list &list) -> py::typing::List<py::typing::Optional<py::str>> {
list.append(py::str("hi"));
list.append(py::none());
return list;
});
m.def("annotate_optional_to_object",
[](py::typing::Optional<int> &o) -> py::object { return o; });

InvincibleRMC marked this conversation as resolved.
Show resolved Hide resolved
#if defined(__cpp_nontype_template_parameter_class)
m.def("annotate_generic_containers",
[](const py::typing::List<typevar::TypeVarT> &l) -> py::typing::List<typevar::TypeVarV> {
return l;
});

m.def("annotate_listT_to_T",
[](const py::typing::List<typevar::TypeVarT> &l) -> typevar::TypeVarT { return l[0]; });
m.def("annotate_object_to_T", [](const py::object &o) -> typevar::TypeVarT { return o; });
m.attr("if_defined__cpp_nontype_template_parameter_class") = true;
#else
m.attr("if_defined__cpp_nontype_template_parameter_class") = false;
#endif
}
31 changes: 30 additions & 1 deletion tests/test_pytypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ def test_fn_annotations(doc):


def test_type_annotation(doc):
assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> None"
assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> type"


def test_union_annotations(doc):
Expand All @@ -975,8 +975,37 @@ def test_union_typing_only(doc):
)


def test_union_object_annotations(doc):
assert (
doc(m.annotate_union_to_object)
== "annotate_union_to_object(arg0: Union[int, str]) -> object"
)


def test_optional_annotations(doc):
assert (
doc(m.annotate_optional)
== "annotate_optional(arg0: list) -> list[Optional[str]]"
)


def test_optional_object_annotations(doc):
assert (
doc(m.annotate_optional_to_object)
== "annotate_optional_to_object(arg0: Optional[int]) -> object"
)


@pytest.mark.skipif(
not m.if_defined__cpp_nontype_template_parameter_class,
reason="C++20 feature not available.",
)
def test_typevar(doc):
assert (
doc(m.annotate_generic_containers)
== "annotate_generic_containers(arg0: list[T]) -> list[V]"
)

assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T"

assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T"
Loading