Skip to content

Commit

Permalink
Provide better type hints for a variety of generic types
Browse files Browse the repository at this point in the history
* Makes better documentation
* tuple, dict, list, set, function
  • Loading branch information
virtuald committed Oct 20, 2022
1 parent fab1eeb commit 8633a05
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/advanced/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,30 @@ before they are used as a parameter or return type of a function:
pyFoo.def(py::init<const ns::Bar&>());
pyBar.def(py::init<const ns::Foo&>());
}
Setting inner type hints in docstrings
======================================

When you use pybind11 wrappers for ``list``, ``dict``, and other generic python
types, the docstring will just display the generic type. You can convey the
inner types in the docstring by using a special 'typed' version of the generic
type.

.. code-block:: cpp
PYBIND11_MODULE(example, m) {
m.def("pass_list_of_str", [](py::List<py::str> arg) {
// arg can be used just like py::list
));
}
The following special types are available:

* ``py::Tuple<Args...>``
* ``py::Dict<K, V>``
* ``py::List<V>``
* ``py::Set<V>``
* ``py::Callable<Signature>``

.. warning:: Just like in python, these are merely hints. They don't actually
enforce the types of their contents at runtime or compile time.
30 changes: 30 additions & 0 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,36 @@ struct handle_type_name<kwargs> {
static constexpr auto name = const_name("**kwargs");
};

template <typename... Types>
struct handle_type_name<Tuple<Types...>> {
static constexpr auto name
= const_name("Tuple[") + concat(make_caster<Types>::name...) + const_name("]");
};

template <typename K, typename V>
struct handle_type_name<Dict<K, V>> {
static constexpr auto name = const_name("Dict[") + make_caster<K>::name + const_name(", ")
+ make_caster<V>::name + const_name("]");
};

template <typename T>
struct handle_type_name<List<T>> {
static constexpr auto name = const_name("List[") + make_caster<T>::name + const_name("]");
};

template <typename T>
struct handle_type_name<Set<T>> {
static constexpr auto name = const_name("Set[") + make_caster<T>::name + const_name("]");
};

template <typename Return, typename... Args>
struct handle_type_name<Callable<Return(Args...)>> {
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
static constexpr auto name = const_name("Callable[[") + concat(make_caster<Args>::name...)
+ const_name("], ") + make_caster<retval_type>::name
+ const_name("]");
};

template <typename type>
struct pyobject_caster {
template <typename T = type, enable_if_t<std::is_same<T, handle>::value, int> = 0>
Expand Down
28 changes: 28 additions & 0 deletions include/pybind11/pytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,11 @@ class tuple : public object {
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
};

template <typename... Types>
class Tuple : public tuple {
using tuple::tuple;
};

// We need to put this into a separate function because the Intel compiler
// fails to compile enable_if_t<all_of<is_keyword_or_ds<Args>...>::value> part below
// (tested with ICC 2021.1 Beta 20200827).
Expand Down Expand Up @@ -1984,6 +1989,11 @@ class dict : public object {
}
};

template <typename K, typename V>
class Dict : public dict {
using dict::dict;
};

class sequence : public object {
public:
PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check)
Expand Down Expand Up @@ -2043,6 +2053,11 @@ class list : public object {
}
};

template <typename T>
class List : public list {
using list::list;
};

class args : public tuple {
PYBIND11_OBJECT_DEFAULT(args, tuple, PyTuple_Check)
};
Expand Down Expand Up @@ -2080,6 +2095,11 @@ class set : public anyset {
void clear() /* py-non-const */ { PySet_Clear(m_ptr); }
};

template <typename T>
class Set : public set {
using set::set;
};

class frozenset : public anyset {
public:
PYBIND11_OBJECT_CVT(frozenset, anyset, PyFrozenSet_Check, PyFrozenSet_New)
Expand All @@ -2098,6 +2118,14 @@ class function : public object {
bool is_cpp_function() const { return (bool) cpp_function(); }
};

template <typename Signature>
class Callable;

template <typename Return, typename... Args>
class Callable<Return(Args...)> : public function {
using function::function;
};

class staticmethod : public object {
public:
PYBIND11_OBJECT_CVT(staticmethod, object, detail::PyStaticMethod_Check, PyStaticMethod_New)
Expand Down
6 changes: 6 additions & 0 deletions tests/test_pytypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -804,4 +804,10 @@ TEST_SUBMODULE(pytypes, m) {
a >>= b;
return a;
});

m.def("annotate_tuple_float_str", [](py::Tuple<py::float_, py::str>) {});
m.def("annotate_dict_str_int", [](py::Dict<py::str, int>) {});
m.def("annotate_list_int", [](py::List<int>) {});
m.def("annotate_set_str", [](py::Set<std::string>) {});
m.def("annotate_fn", [](py::Callable<int(py::List<py::str>, py::str)>) {});
}
29 changes: 29 additions & 0 deletions tests/test_pytypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,3 +863,32 @@ def test_inplace_lshift(a, b):
def test_inplace_rshift(a, b):
expected = a >> b
assert m.inplace_rshift(a, b) == expected


def test_tuple_annotations(doc):
assert (
doc(m.annotate_tuple_float_str)
== "annotate_tuple_float_str(arg0: Tuple[float, str]) -> None"
)


def test_dict_annotations(doc):
assert (
doc(m.annotate_dict_str_int)
== "annotate_dict_str_int(arg0: Dict[str, int]) -> None"
)


def test_list_annotations(doc):
assert doc(m.annotate_list_int) == "annotate_list_int(arg0: List[int]) -> None"


def test_set_annotations(doc):
assert doc(m.annotate_set_str) == "annotate_set_str(arg0: Set[str]) -> None"


def test_fn_annotations(doc):
assert (
doc(m.annotate_fn)
== "annotate_fn(arg0: Callable[[List[str], str], int]) -> None"
)

0 comments on commit 8633a05

Please sign in to comment.