Fix Backtrackable.can_peek_back off-by-one contract violation (#4065)

`can_peek_back(steps)` is documented to return whether `peek_back(steps)`
can be called "without raising an IndexError", but it guarded with `<=`:

    return steps <= len(self._back_buf) + self._cursor

`peek_back(n)` needs n+1 buffered slots — it raises when
`n + 1 > len(self._back_buf) + self._cursor` and reads
`self._back_buf[self._cursor - (n + 1)]`. So at
`steps == len(self._back_buf) + self._cursor`, `can_peek_back` returns True
while `peek_back` raises LookBackError, contradicting the docstring.

Two siblings confirm the intended bound:
- `prev()` (one step back) requires `len(self._back_buf) + self._cursor > 1`.
- The forward twin is already consistent: `can_peek_ahead(n)` buffers n items
  and `peek_ahead(n)` reads `_ahead_buf[n - 1]` (needs n).

Use `<` so `can_peek_back` matches `peek_back`'s guard exactly.

Co-authored-by: Steven Palma <imstevenpmwork@ieee.org>
This commit is contained in:
Syed Osama Ali Shah
2026-07-30 16:53:34 +03:00
committed by GitHub
parent 7e0fd0d653
commit d632a103ae
+1 -1
View File
@@ -178,7 +178,7 @@ class Backtrackable[T]:
""" """
Check if we can go back `steps` items without raising an IndexError. Check if we can go back `steps` items without raising an IndexError.
""" """
return steps <= len(self._back_buf) + self._cursor return steps < len(self._back_buf) + self._cursor
def can_peek_ahead(self, steps: int = 1) -> bool: def can_peek_ahead(self, steps: int = 1) -> bool:
""" """