Skip to content

Commit 9531c6f

Browse files
committed
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()
1 parent eeb1044 commit 9531c6f

File tree

3 files changed

+59
-8
lines changed

3 files changed

+59
-8
lines changed

include/pybind11/pybind11.h

+29-7
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,23 @@ struct iterator_state {
16801680
bool first_or_done;
16811681
};
16821682

1683+
template<typename Iterator, typename Sentinel, return_value_policy Policy>
1684+
struct type_caster<iterator_state<Iterator, Sentinel, false, Policy>> :
1685+
type_caster_base<iterator_state<Iterator, Sentinel, false, Policy>> {
1686+
using ValueType = decltype(*std::declval<Iterator>());
1687+
public:
1688+
static constexpr auto name = _("Iterator[") + make_caster<ValueType>::name + _("]");
1689+
};
1690+
1691+
template<typename Iterator, typename Sentinel, return_value_policy Policy>
1692+
struct type_caster<iterator_state<Iterator, Sentinel, true, Policy>> :
1693+
type_caster_base<iterator_state<Iterator, Sentinel, true, Policy>> {
1694+
using ValueType = decltype((*std::declval<Iterator>()).first);
1695+
public:
1696+
static constexpr auto name = _("Iterator[") + make_caster<ValueType>::name + _("]");
1697+
};
1698+
1699+
16831700
NAMESPACE_END(detail)
16841701

16851702
/// Makes a python iterator from a first and past-the-end C++ InputIterator.
@@ -1688,7 +1705,8 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
16881705
typename Sentinel,
16891706
typename ValueType = decltype(*std::declval<Iterator>()),
16901707
typename... Extra>
1691-
iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
1708+
detail::iterator_state<Iterator, Sentinel, false, Policy>
1709+
make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
16921710
typedef detail::iterator_state<Iterator, Sentinel, false, Policy> state;
16931711

16941712
if (!detail::get_type_info(typeid(state), false)) {
@@ -1706,8 +1724,7 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
17061724
return *s.it;
17071725
}, std::forward<Extra>(extra)..., Policy);
17081726
}
1709-
1710-
return cast(state{first, last, true});
1727+
return state{first, last, true};
17111728
}
17121729

17131730
/// Makes an python iterator over the keys (`.first`) of a iterator over pairs from a
@@ -1717,7 +1734,8 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
17171734
typename Sentinel,
17181735
typename KeyType = decltype((*std::declval<Iterator>()).first),
17191736
typename... Extra>
1720-
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
1737+
detail::iterator_state<Iterator, Sentinel, true, Policy>
1738+
make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
17211739
typedef detail::iterator_state<Iterator, Sentinel, true, Policy> state;
17221740

17231741
if (!detail::get_type_info(typeid(state), false)) {
@@ -1736,20 +1754,24 @@ iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
17361754
}, std::forward<Extra>(extra)..., Policy);
17371755
}
17381756

1739-
return cast(state{first, last, true});
1757+
return state{first, last, true};
17401758
}
17411759

17421760
/// Makes an iterator over values of an stl container or other container supporting
17431761
/// `std::begin()`/`std::end()`
17441762
template <return_value_policy Policy = return_value_policy::reference_internal,
1745-
typename Type, typename... Extra> iterator make_iterator(Type &value, Extra&&... extra) {
1763+
typename Type, typename... Extra>
1764+
detail::iterator_state<decltype(std::begin(std::declval<Type&>())), decltype(std::end(std::declval<Type&>())), false, Policy>
1765+
make_iterator(Type &value, Extra&&... extra) {
17461766
return make_iterator<Policy>(std::begin(value), std::end(value), extra...);
17471767
}
17481768

17491769
/// Makes an iterator over the keys (`.first`) of a stl map-like container supporting
17501770
/// `std::begin()`/`std::end()`
17511771
template <return_value_policy Policy = return_value_policy::reference_internal,
1752-
typename Type, typename... Extra> iterator make_key_iterator(Type &value, Extra&&... extra) {
1772+
typename Type, typename... Extra>
1773+
detail::iterator_state<decltype(std::begin(std::declval<Type&>())), decltype(std::end(std::declval<Type&>())), true, Policy>
1774+
make_key_iterator(Type &value, Extra&&... extra) {
17531775
return make_key_iterator<Policy>(std::begin(value), std::end(value), extra...);
17541776
}
17551777

tests/test_sequences_and_iterators.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
344344

345345
// test_iterator_passthrough
346346
// #181: iterator passthrough did not compile
347-
m.def("iterator_passthrough", [](py::iterator s) -> py::iterator {
347+
m.def("iterator_passthrough", [](py::iterator s) {
348348
return py::make_iterator(std::begin(s), std::end(s));
349349
});
350350

tests/test_stl_binders.py

+29
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,32 @@ def test_map_delitem():
275275
del um['ua']
276276
assert sorted(list(um)) == ['ub']
277277
assert sorted(list(um.items())) == [('ub', 2.6)]
278+
279+
280+
def test_map_docstrings(doc):
281+
assert (doc(m.MapStringDouble.__iter__) ==
282+
"__iter__(self: m.stl_binders.MapStringDouble)"
283+
" -> Iterator[str]")
284+
assert (doc(m.MapStringDouble.items) ==
285+
"items(self: m.stl_binders.MapStringDouble)"
286+
" -> Iterator[Tuple[str, float]]")
287+
assert (doc(m.UnorderedMapStringDouble.__iter__) ==
288+
"__iter__(self: m.stl_binders.UnorderedMapStringDouble)"
289+
" -> Iterator[str]\n")
290+
assert (doc(m.UnorderedMapStringDouble.items) ==
291+
"items(self: m.stl_binders.UnorderedMapStringDouble)"
292+
" -> Iterator[Tuple[str, float]]\n")
293+
294+
295+
def test_vector_docstrings(doc):
296+
assert (doc(m.VectorInt.__iter__) ==
297+
"__iter__(self: m.stl_binders.VectorInt)"
298+
" -> Iterator[int]\n")
299+
300+
301+
@pytest.unsupported_on_pypy
302+
@pytest.requires_numpy
303+
def test_vector_docstring2(doc):
304+
assert (doc(m.VectorStruct.__iter__) ==
305+
"__iter__(self: m.stl_binders.VectorStruct)"
306+
" -> Iterator[m.stl_binders.VStruct]")

0 commit comments

Comments
 (0)