Loop commands until they behave.
Installation • Quick Start • Usage • API
A minimal CLI tool to execute commands in a loop with smart stop conditions. Perfect for polling, retrying, and automation tasks.
pip install -e .# Poll until a service is ready
luper -c "curl -sf localhost:8080/health" --until-success -d 2
# Run tests until they fail (find flaky tests)
luper -c "pytest" --until-error
# Execute a sequence 5 times
luper -c "echo 'Step 1'" -c "echo 'Step 2'" -n 5luper -c <command> [-c <command>...] [options]
| Flag | Description |
|---|---|
-c, --command |
Command to run (repeatable) |
-n, --iterations |
Stop after N iterations |
--forever |
Run indefinitely |
--until-success |
Stop when all commands succeed |
--until-error |
Stop when any command fails |
-d, --delay |
Seconds to wait between iterations |
-q, --quiet |
Silent mode |
Wait for a database to come online:
luper -c "pg_isready -h localhost" --until-success -d 1Retry a flaky deploy:
luper -c "./deploy.sh" --until-success -n 3Monitor a log file every 5 seconds:
luper -c "tail -1 /var/log/app.log" --forever -d 5Run a build + test pipeline in a loop:
luper -c "make build" -c "make test" -n 10| Signal | Behavior |
|---|---|
Ctrl+C |
Graceful stop after current iteration |
Ctrl+C ×2 |
Force immediate exit |
Use luper programmatically in your Python scripts:
from luper import Runner
# Basic loop
runner = Runner(
commands=["echo hello", "echo world"],
iterations=5,
)
results = runner.run()
# Poll until success
runner = Runner(
commands=["curl -sf localhost:8080"],
stop_on_success=True,
delay=2.0,
)
results = runner.run()
# Custom stop condition
runner = Runner(
commands=["./check_status.sh"],
stop_condition=lambda r: "READY" in r.results[0].stdout,
)
results = runner.run()
# Inspect results
for iteration in results:
status = "PASS" if iteration.all_success else "FAIL"
print(f"[{status}] Iteration {iteration.iteration}")# IterationResult
iteration.iteration # int: iteration number (0-indexed)
iteration.results # list[CommandResult]: results for each command
iteration.total_duration # float: total time in seconds
iteration.all_success # bool: True if all commands succeeded
iteration.any_error # bool: True if any command failed
# CommandResult
result.command # str: the command that was run
result.return_code # int: exit code
result.stdout # str: captured stdout
result.stderr # str: captured stderr
result.duration # float: execution time in seconds
result.success # bool: True if return_code == 0MIT
Dedicated to Lupe Quesada