diff --git a/include/pybind11/stl_bind.h b/include/pybind11/stl_bind.h index 8202300c70..5d47aacb16 100644 --- a/include/pybind11/stl_bind.h +++ b/include/pybind11/stl_bind.h @@ -286,18 +286,25 @@ void vector_modifiers( cl.def( "__delitem__", [](Vector &v, const slice &slice) { - size_t start = 0, stop = 0, step = 0, slicelength = 0; + ssize_t start = 0, stop = 0, step = 0, slicelength = 0; - if (!slice.compute(v.size(), &start, &stop, &step, &slicelength)) { + if (!slice.compute( + static_cast(v.size()), &start, &stop, &step, &slicelength)) { throw error_already_set(); } - if (step == 1 && false) { + if (step == 1) { v.erase(v.begin() + (DiffType) start, v.begin() + DiffType(start + slicelength)); } else { - for (size_t i = 0; i < slicelength; ++i) { + // For a positive step, erasing an element shifts the remaining + // (later) elements down by one, so the next index to erase is + // ``start + step - 1``. For a negative step the visited indices + // are strictly decreasing, so erasing never shifts them and the + // next index is simply ``start + step``. + ssize_t offset = step > 0 ? step - 1 : step; + for (ssize_t i = 0; i < slicelength; ++i) { v.erase(v.begin() + DiffType(start)); - start += step - 1; + start += offset; } } }, diff --git a/tests/test_stl_binders.py b/tests/test_stl_binders.py index 518f2df2b6..7836a5a55b 100644 --- a/tests/test_stl_binders.py +++ b/tests/test_stl_binders.py @@ -67,6 +67,31 @@ def test_vector_int(): assert len(v_int2) == 0 +@pytest.mark.parametrize( + "s", + [ + slice(1, 4), + slice(None, None, 2), + slice(1, None, 2), + slice(None, None, -1), + slice(None, None, -2), + slice(3, 1, -1), + slice(2, 2), + slice(None), + slice(5, 0, -2), + slice(-3, -1), + slice(None, None, -3), + ], +) +def test_vector_delitem_slice(s): + for n in range(8): + ref = list(range(n)) + got = m.VectorInt(range(n)) + del ref[s] + del got[s] + assert list(got) == ref, f"n={n}" + + # Older PyPy's failed here, related to the PyPy's buffer protocol. def test_vector_buffer(): b = bytearray([1, 2, 3, 4])