Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changelog/010.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: console
ts: 2026-03-18 07:44:00.342408+00:00
type: fix
author: Espen Albert
changelog_message: 'fix(console): Treat zero exit code exceptions as success in progress tracking'
message: 'fix(console): Treat zero exit code exceptions as success in progress tracking'
short_sha: fe4ffb
14 changes: 12 additions & 2 deletions ask_shell/_internal/rich_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ def transient_progress() -> Progress:
)


def _is_clean_exit(exc: BaseException | None) -> bool:
if exc is None:
return True
if isinstance(exc, SystemExit) and exc.code in (None, 0):
return True
exit_code = getattr(exc, "exit_code", None)
return exit_code == 0


def log_task_done(
task: new_task,
*,
Expand Down Expand Up @@ -206,5 +215,6 @@ def __enter__(self) -> Self:
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.complete() # Ensure we mark it as complete
self.manager.remove_task(self, error=exc_val)
self.complete()
error = exc_val if not _is_clean_exit(exc_val) else None
self.manager.remove_task(self, error=error)
14 changes: 12 additions & 2 deletions ask_shell/public_api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,19 @@ def test_multiple_attempts(tmp_path):
"""error might look weird due to flushing"""
script_path = tmp_path / "attempt.py"
script_path.write_text(_attempt_script)
result = run_and_wait(ShellConfig(shell_input=f"{PYTHON_EXEC} {script_path}", attempts=3))
result = run_and_wait(
ShellConfig(shell_input=f"{PYTHON_EXEC} {script_path}", attempts=3, retry_initial_wait=0, retry_jitter=0)
)
assert result.clean_complete


def test_not_enough_attempts(tmp_path):
script_path = tmp_path / "attempt.py"
script_path.write_text(_attempt_script)
with pytest.raises(ShellError) as exc:
run_and_wait(ShellConfig(shell_input=f"{PYTHON_EXEC} {script_path}", attempts=2))
run_and_wait(
ShellConfig(shell_input=f"{PYTHON_EXEC} {script_path}", attempts=2, retry_initial_wait=0, retry_jitter=0)
)
assert "attempt in script: 2/3" in exc.value.stdout
assert exc.value.exit_code == 1

Expand All @@ -142,6 +146,8 @@ def never_retry(_):
shell_input=f"{PYTHON_EXEC} {script_path}",
attempts=4,
should_retry=never_retry,
retry_initial_wait=0,
retry_jitter=0,
)
)
assert "attempt in script: 1/3" in exc.value.stdout
Expand All @@ -159,6 +165,8 @@ def retry_if_attempt(run: ShellRun):
shell_input=f"{PYTHON_EXEC} {script_path}",
attempts=4,
should_retry=retry_if_attempt,
retry_initial_wait=0,
retry_jitter=0,
)
)
assert "attempt in script: 3/3" in result.stdout
Expand All @@ -177,6 +185,8 @@ def abort_on_first_failure(run: ShellRun) -> bool:
shell_input=f"{PYTHON_EXEC} {script_path}",
attempts=4,
should_retry=abort_on_first_failure,
retry_initial_wait=0,
retry_jitter=0,
)
)
assert isinstance(exc.value.base_error, AbortRetryError)
Expand Down
2 changes: 1 addition & 1 deletion docs/console/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def log_to_live(*objects, sep: str = ' ', end: str = '\n', style: str | Style |
<a id="new_task_def"></a>

### class: `new_task`
- [source](../../ask_shell/_internal/rich_progress.py#L153)
- [source](../../ask_shell/_internal/rich_progress.py#L162)
> **Since:** 0.3.0

```python
Expand Down
3 changes: 3 additions & 0 deletions docs/examples/shell/AbortRetryError.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ result = run_and_wait(
shell_input=f"{PYTHON_EXEC} {tmp / 'run.py'}",
attempts=3,
should_retry=should_retry_transient,
retry_initial_wait=0.01,
retry_max_wait=0.1,
retry_jitter=0,
)
)
print(result.stdout)
Expand Down
12 changes: 6 additions & 6 deletions docs/examples/shell/backoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ with TemporaryDirectory() as tmp:
result = run_and_wait(
f"{sys.executable} {script_path}",
attempts=4,
retry_initial_wait=0.1,
retry_max_wait=10,
retry_initial_wait=0.01,
retry_max_wait=1,
retry_jitter=0,
)
elapsed = time.monotonic() - start
print(result.stdout)
#> ok on attempt 4
print(f"waited at least 0.7s: {elapsed >= 0.7}") # 0.1+0.2+0.4
#> waited at least 0.7s: True
print(f"waited less than 2s: {elapsed < 2}")
#> waited less than 2s: True
print(f"waited at least 0.07s: {elapsed >= 0.07}") # 0.01 + 0.02 + 0.04
#> waited at least 0.07s: True
print(f"waited less than 1s: {elapsed < 1}")
#> waited less than 1s: True
```
Loading