From 9531c6faadeb98cabdd7763900efbc0ce866c1aa Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Thu, 14 May 2020 23:16:01 +0300 Subject: [PATCH] Render typed iterators (e.g. Iterator[int]) in docstrings This commit introduces minor breaking change: `make_iterator` and `make_key_iterator` now return `iterator_state` instance instead of `py::iterator`. It doesn't affect regular use of those functions (immediate return from __iter__ lambda), but requires changes in user code with implicit assignments/conversions, e.g.: py::iterator it = make_iterator(...); # requires explicit py::cast() --- include/pybind11/pybind11.h | 36 +++++++++++++++++++++----- tests/test_sequences_and_iterators.cpp | 2 +- tests/test_stl_binders.py | 29 +++++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index dee2423de0..8273bf1c62 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1680,6 +1680,23 @@ struct iterator_state { bool first_or_done; }; +template +struct type_caster> : + type_caster_base> { + using ValueType = decltype(*std::declval()); +public: + static constexpr auto name = _("Iterator[") + make_caster::name + _("]"); +}; + +template +struct type_caster> : + type_caster_base> { + using ValueType = decltype((*std::declval()).first); +public: + static constexpr auto name = _("Iterator[") + make_caster::name + _("]"); +}; + + NAMESPACE_END(detail) /// Makes a python iterator from a first and past-the-end C++ InputIterator. @@ -1688,7 +1705,8 @@ template ()), typename... Extra> -iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { +detail::iterator_state + make_iterator(Iterator first, Sentinel last, Extra &&... extra) { typedef detail::iterator_state state; if (!detail::get_type_info(typeid(state), false)) { @@ -1706,8 +1724,7 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) { return *s.it; }, std::forward(extra)..., Policy); } - - return cast(state{first, last, true}); + return state{first, last, true}; } /// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a @@ -1717,7 +1734,8 @@ template ()).first), typename... Extra> -iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { +detail::iterator_state + make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { typedef detail::iterator_state state; if (!detail::get_type_info(typeid(state), false)) { @@ -1736,20 +1754,24 @@ iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) { }, std::forward(extra)..., Policy); } - return cast(state{first, last, true}); + return state{first, last, true}; } /// Makes an iterator over values of an stl container or other container supporting /// `std::begin()`/`std::end()` template iterator make_iterator(Type &value, Extra&&... extra) { + typename Type, typename... Extra> +detail::iterator_state())), decltype(std::end(std::declval())), false, Policy> +make_iterator(Type &value, Extra&&... extra) { return make_iterator(std::begin(value), std::end(value), extra...); } /// Makes an iterator over the keys (`.first`) of a stl map-like container supporting /// `std::begin()`/`std::end()` template iterator make_key_iterator(Type &value, Extra&&... extra) { + typename Type, typename... Extra> +detail::iterator_state())), decltype(std::end(std::declval())), true, Policy> +make_key_iterator(Type &value, Extra&&... extra) { return make_key_iterator(std::begin(value), std::end(value), extra...); } diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 05f999bb3b..70999ffd84 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -344,7 +344,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) { // test_iterator_passthrough // #181: iterator passthrough did not compile - m.def("iterator_passthrough", [](py::iterator s) -> py::iterator { + m.def("iterator_passthrough", [](py::iterator s) { return py::make_iterator(std::begin(s), std::end(s)); }); diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index c1264c01f2..292fa065f1 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -275,3 +275,32 @@ def test_map_delitem(): del um['ua'] assert sorted(list(um)) == ['ub'] assert sorted(list(um.items())) == [('ub', 2.6)] + + +def test_map_docstrings(doc): + assert (doc(m.MapStringDouble.__iter__) == + "__iter__(self: m.stl_binders.MapStringDouble)" + " -> Iterator[str]") + assert (doc(m.MapStringDouble.items) == + "items(self: m.stl_binders.MapStringDouble)" + " -> Iterator[Tuple[str, float]]") + assert (doc(m.UnorderedMapStringDouble.__iter__) == + "__iter__(self: m.stl_binders.UnorderedMapStringDouble)" + " -> Iterator[str]\n") + assert (doc(m.UnorderedMapStringDouble.items) == + "items(self: m.stl_binders.UnorderedMapStringDouble)" + " -> Iterator[Tuple[str, float]]\n") + + +def test_vector_docstrings(doc): + assert (doc(m.VectorInt.__iter__) == + "__iter__(self: m.stl_binders.VectorInt)" + " -> Iterator[int]\n") + + +@pytest.unsupported_on_pypy +@pytest.requires_numpy +def test_vector_docstring2(doc): + assert (doc(m.VectorStruct.__iter__) == + "__iter__(self: m.stl_binders.VectorStruct)" + " -> Iterator[m.stl_binders.VStruct]")