diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 4003d691..63cbf2e5 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1242,7 +1242,12 @@ private: class sequence : public object { public: PYBIND11_OBJECT_DEFAULT(sequence, object, PySequence_Check) - size_t size() const { return (size_t) PySequence_Size(m_ptr); } + size_t size() const { + ssize_t result = PySequence_Size(m_ptr); + if (result == -1) + throw error_already_set(); + return (size_t) result; + } bool empty() const { return size() == 0; } detail::sequence_accessor operator[](size_t index) const { return {*this, index}; } detail::item_accessor operator[](handle h) const { return object::operator[](h); } diff --git a/tests/test_sequences_and_iterators.cpp b/tests/test_sequences_and_iterators.cpp index 87ccf99d..05f999bb 100644 --- a/tests/test_sequences_and_iterators.cpp +++ b/tests/test_sequences_and_iterators.cpp @@ -319,6 +319,9 @@ TEST_SUBMODULE(sequences_and_iterators, m) { return l; }); + // test_sequence_length: check that Python sequences can be converted to py::sequence. + m.def("sequence_length", [](py::sequence seq) { return seq.size(); }); + // Make sure that py::iterator works with std algorithms m.def("count_none", [](py::object o) { return std::count_if(o.begin(), o.end(), [](py::handle h) { return h.is_none(); }); diff --git a/tests/test_sequences_and_iterators.py b/tests/test_sequences_and_iterators.py index 6bd16064..d839dbef 100644 --- a/tests/test_sequences_and_iterators.py +++ b/tests/test_sequences_and_iterators.py @@ -100,6 +100,25 @@ def test_sequence(): assert cstats.move_assignments == 0 +def test_sequence_length(): + """#2076: Exception raised by len(arg) should be propagated """ + class BadLen(RuntimeError): + pass + + class SequenceLike(): + def __getitem__(self, i): + return None + + def __len__(self): + raise BadLen() + + with pytest.raises(BadLen): + m.sequence_length(SequenceLike()) + + assert m.sequence_length([1, 2, 3]) == 3 + assert m.sequence_length("hello") == 5 + + def test_map_iterator(): sm = m.StringMap({'hi': 'bye', 'black': 'white'}) assert sm['hi'] == 'bye'