ask-shell 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ask_shell-0.5.0 → ask_shell-0.5.2}/PKG-INFO +1 -1
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/__init__.py +1 -1
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/_run.py +22 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/models.py +3 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/rich_progress.py +12 -2
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/test_docs.py +1 -1
- {ask_shell-0.5.0 → ask_shell-0.5.2}/pyproject.toml +1 -1
- {ask_shell-0.5.0 → ask_shell-0.5.2}/.gitignore +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/LICENSE +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/README.md +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/__main__.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/__init__.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/_run_env.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/events.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/global_callbacks.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/interactive.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/rich_live.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/rich_live_callback.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/rich_run_state.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/run_pool.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/_internal/typer_command.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/ask.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/console.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/py.typed +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/settings.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/shell.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/ask_shell/shell_events.py +0 -0
- {ask_shell-0.5.0 → ask_shell-0.5.2}/scripts/fix_source_links.py +0 -0
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
import atexit
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
+
import random
|
|
16
17
|
import signal
|
|
17
18
|
import subprocess
|
|
18
19
|
import sys
|
|
@@ -382,11 +383,20 @@ def _eval_should_retry(config: ShellConfig, result: ShellRun) -> bool:
|
|
|
382
383
|
return False
|
|
383
384
|
|
|
384
385
|
|
|
386
|
+
def _backoff_wait(attempt: int, config: ShellConfig) -> float:
|
|
387
|
+
"""wait = min(initial * 2^(attempt-2), max_wait) + uniform(0, jitter)"""
|
|
388
|
+
base = min(config.retry_initial_wait * 2 ** (attempt - 2), config.retry_max_wait)
|
|
389
|
+
return base + random.uniform(0, config.retry_jitter)
|
|
390
|
+
|
|
391
|
+
|
|
385
392
|
def _run_attempts(shell_run: ShellRun, output_dir: Path) -> BaseException | None:
|
|
386
393
|
config = shell_run.config
|
|
387
394
|
queue = shell_run._queue
|
|
388
395
|
for attempt in range(1, config.attempts + 1):
|
|
389
396
|
if attempt > 1:
|
|
397
|
+
wait_seconds = _backoff_wait(attempt, config)
|
|
398
|
+
logger.info(f"Backoff {wait_seconds:.2f}s before attempt {attempt}/{config.attempts}")
|
|
399
|
+
time.sleep(wait_seconds)
|
|
390
400
|
queue.put_nowait(ShellRunRetryAttempt(attempt=attempt))
|
|
391
401
|
logger.warning(f"Retrying run {shell_run} attempt {attempt} of {config.attempts}")
|
|
392
402
|
attempt_log_prefix = config.run_log_stem(attempt)
|
|
@@ -459,6 +469,9 @@ def run(
|
|
|
459
469
|
is_binary_call: bool | None = None,
|
|
460
470
|
message_callbacks: list[Callable[[ShellRunEventT], bool]] | None = None,
|
|
461
471
|
print_prefix: str | None = None,
|
|
472
|
+
retry_initial_wait: float | None = None,
|
|
473
|
+
retry_jitter: float | None = None,
|
|
474
|
+
retry_max_wait: float | None = None,
|
|
462
475
|
run_log_stem_prefix: str | None = None,
|
|
463
476
|
run_output_dir: Path | None | None = None,
|
|
464
477
|
settings: AskShellSettings | None = None,
|
|
@@ -483,6 +496,9 @@ def run(
|
|
|
483
496
|
is_binary_call=is_binary_call,
|
|
484
497
|
message_callbacks=message_callbacks,
|
|
485
498
|
print_prefix=print_prefix,
|
|
499
|
+
retry_initial_wait=retry_initial_wait,
|
|
500
|
+
retry_jitter=retry_jitter,
|
|
501
|
+
retry_max_wait=retry_max_wait,
|
|
486
502
|
run_log_stem_prefix=run_log_stem_prefix,
|
|
487
503
|
run_output_dir=run_output_dir,
|
|
488
504
|
settings=settings,
|
|
@@ -518,6 +534,9 @@ def run_and_wait(
|
|
|
518
534
|
is_binary_call: bool | None = None,
|
|
519
535
|
message_callbacks: list[Callable[[ShellRunEventT], bool]] | None = None,
|
|
520
536
|
print_prefix: str | None = None,
|
|
537
|
+
retry_initial_wait: float | None = None,
|
|
538
|
+
retry_jitter: float | None = None,
|
|
539
|
+
retry_max_wait: float | None = None,
|
|
521
540
|
run_log_stem_prefix: str | None = None,
|
|
522
541
|
run_output_dir: Path | None | None = None,
|
|
523
542
|
settings: AskShellSettings | None = None,
|
|
@@ -542,6 +561,9 @@ def run_and_wait(
|
|
|
542
561
|
is_binary_call=is_binary_call,
|
|
543
562
|
message_callbacks=message_callbacks,
|
|
544
563
|
print_prefix=print_prefix,
|
|
564
|
+
retry_initial_wait=retry_initial_wait,
|
|
565
|
+
retry_jitter=retry_jitter,
|
|
566
|
+
retry_max_wait=retry_max_wait,
|
|
545
567
|
run_log_stem_prefix=run_log_stem_prefix,
|
|
546
568
|
run_output_dir=run_output_dir,
|
|
547
569
|
settings=settings,
|
|
@@ -151,6 +151,9 @@ class ShellConfig(Entity):
|
|
|
151
151
|
# retry args
|
|
152
152
|
attempts: int = 1
|
|
153
153
|
should_retry: Callable[[ShellRun], bool] = always_retry
|
|
154
|
+
retry_initial_wait: float = Field(default=5, description="Seconds before first retry, only used when attempts > 1")
|
|
155
|
+
retry_max_wait: float = Field(default=60, description="Cap on wait between retries")
|
|
156
|
+
retry_jitter: float = Field(default=5, description="Max random jitter added to wait")
|
|
154
157
|
|
|
155
158
|
# logging/output
|
|
156
159
|
print_prefix: str = Field(default=None, description="Use cwd+binary_name+first_arg if not provided") # type: ignore
|
|
@@ -31,6 +31,15 @@ def transient_progress() -> Progress:
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def _is_clean_exit(exc: BaseException | None) -> bool:
|
|
35
|
+
if exc is None:
|
|
36
|
+
return True
|
|
37
|
+
if isinstance(exc, SystemExit) and exc.code in (None, 0):
|
|
38
|
+
return True
|
|
39
|
+
exit_code = getattr(exc, "exit_code", None)
|
|
40
|
+
return exit_code == 0
|
|
41
|
+
|
|
42
|
+
|
|
34
43
|
def log_task_done(
|
|
35
44
|
task: new_task,
|
|
36
45
|
*,
|
|
@@ -206,5 +215,6 @@ class new_task:
|
|
|
206
215
|
return self
|
|
207
216
|
|
|
208
217
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
209
|
-
self.complete()
|
|
210
|
-
|
|
218
|
+
self.complete()
|
|
219
|
+
error = exc_val if not _is_clean_exit(exc_val) else None
|
|
220
|
+
self.manager.remove_task(self, error=error)
|
|
@@ -8,7 +8,7 @@ EXAMPLES_DIR = Path(__file__).parent.parent / "docs" / "examples"
|
|
|
8
8
|
|
|
9
9
|
@pytest.mark.parametrize("example", find_examples(EXAMPLES_DIR), ids=str)
|
|
10
10
|
def test_examples(example: CodeExample, eval_example: EvalExample):
|
|
11
|
-
eval_example.set_config(line_length=120, target_version="py313", ruff_ignore=["T"]) # pyright: ignore[reportArgumentType]
|
|
11
|
+
eval_example.set_config(line_length=120, target_version="py313", ruff_ignore=["I001", "T"]) # pyright: ignore[reportArgumentType]
|
|
12
12
|
prefix = example.prefix_settings()
|
|
13
13
|
if prefix.get("test", "").startswith("skip"):
|
|
14
14
|
pytest.skip(prefix["test"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|