Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.3.1 (TBD)

- Fixed history ranges starting past the end selecting the last command, including when using
`history --run`.

## 4.3.0 (October 13, 2026)

- Stabilized features
Expand Down
2 changes: 1 addition & 1 deletion cmd2/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def span(self, span: str, include_persisted: bool = False) -> dict[int, "History

start_token = results.group("start")
if start_token:
start = min(self._zero_based_index(start_token), len(self) - 1)
start = min(self._zero_based_index(start_token), len(self))
if start < 0:
start = max(0, len(self) + start)
else:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,19 @@ def test_history_class_span(hist) -> None:
hist.span(tryit)


@pytest.mark.parametrize("suffix", [":", ":999", ":-1", "..", "..999", "..-1"])
def test_history_span_past_end(hist, suffix) -> None:
assert hist.span(f"{len(hist) + 1}{suffix}") == {}


@pytest.mark.parametrize(
("span", "indices"),
[("4:", [4]), ("1:999", [1, 2, 3, 4]), ("-999:-1", [1, 2, 3, 4]), ("999:1", [])],
)
def test_history_span_boundaries(hist, span, indices) -> None:
assert list(hist.span(span)) == indices


def test_persisted_history_span(persisted_hist) -> None:
span = persisted_hist.span("2..")
assert len(span) == 5
Expand Down Expand Up @@ -644,6 +657,20 @@ def test_history_with_span_end(base_app) -> None:
verify_hi_last_result(base_app, 2)


@pytest.mark.parametrize("span", ["2:", "2:3", "2..", "2..3"])
def test_history_run_span_past_end(base_app, span) -> None:
run_cmd(base_app, "set debug true")
assert len(base_app.history) == 1
base_app.debug = False

out, err = run_cmd(base_app, f"history --run {span}")

assert base_app.debug is False
assert out == []
assert err == []
assert len(base_app.history) == 1


def test_history_with_span_index_error(base_app) -> None:
run_cmd(base_app, "help")
run_cmd(base_app, "help history")
Expand Down
Loading