#13468·slint

Fix ListModel.__delitem__ wrong row-removed notification for stepped slices

Author: pamod-madubashanaCreated Sep 17, 2026Updated Sep 17, 2026
Labelsneed triaging

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:

python
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 row

Expected 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 single notify_row_removed(0, 3), i.e. "rows 0,1,2 removed" — the view removes the wrong rows (see api/python/slint/slint/models.py:145).
  • del m[-1] removes the last element but notifies notify_row_removed(-1, 1) instead of the normalized index (see api/python/slint/slint/models.py:148).

Evidence

  • api/python/slint/slint/models.py:140-148 (__delitem__: count = len(range(start, stop, step)) then notify_row_removed(start, count) assumes contiguity; else branch forwards key un-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