Fix ListModel.__delitem__ wrong row-removed notification for stepped slices
Summary
ListModel.__delitem__ in the Python API sends an incorrect notify_row_removed notification when deleting with an extended slice (step != 1). The native notification means "count contiguous rows removed starting at row" (api/python/slint/models.rs:129, fn notify_row_removed(&self, index: usize, count: usize)), but for a stepped delete the removed rows are not contiguous, so bound views drop the wrong rows.
The same method also forwards a negative index unchanged for del model[-1] (notify_row_removed(-1, 1)), which cannot be a valid usize row on the Rust side.
Steps to reproduce
Stub the native base class (so no compiled extension is needed) and exercise the real api/python/slint/slint/models.py code:
import pathlib
MODELS = pathlib.Path("api/python/slint/slint/models.py")
src = MODELS.read_text().replace("from ._native import native", "native = FakeNative()")
calls = []
class FakeBase:
def notify_row_changed(self, row): calls.append(("changed", row))
def notify_row_removed(self, row, count): calls.append(("removed", row, count))
def notify_row_added(self, row, count): calls.append(("added", row, count))
class FakeNative:
PyModelBase = FakeBase
ns = {"FakeNative": FakeNative}
exec(compile(src, str(MODELS), "exec"), ns)
ListModel = ns["ListModel"]
m = ListModel([0, 1, 2, 3, 4])
del m[::2]
print(list(m), calls) # [1, 3] [('removed', 0, 3)] <- wrong notification
m2 = ListModel([10, 20, 30])
calls.clear()
del m2[-1]
print(list(m2), calls) # [10, 20] [('removed', -1, 1)] <- negative rowExpected vs actual
del m[::2]on[0,1,2,3,4]removes indices 0, 2, 4 (non-contiguous). Expected: notifications describing exactly those rows (e.g. three single-row removals, highest index first). Actual: a singlenotify_row_removed(0, 3), i.e. "rows 0,1,2 removed" — the view removes the wrong rows (seeapi/python/slint/slint/models.py:145).del m[-1]removes the last element but notifiesnotify_row_removed(-1, 1)instead of the normalized index (seeapi/python/slint/slint/models.py:148).
Evidence
api/python/slint/slint/models.py:140-148(__delitem__:count = len(range(start, stop, step))thennotify_row_removed(start, count)assumes contiguity;elsebranch forwardskeyun-normalized).- Contiguous semantics of the notification:
api/python/slint/models.rs:129. - Existing tests only cover the contiguous
del model[1:]path (api/python/slint/tests/test_models.py:109), so no test pins the buggy behavior.
This issue was filed automatically by an unattended agent (OpenCode) after an automated source audit of this repository. It has not been reviewed by a human. The evidence and reproduction are in the report above. A draft fix is being prepared and will be linked here.
Workflow run: https://github.com/pamod-madubashana/Issue-Hunter/actions/runs/35273631887
Source: slint-ui/slint