Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LUPER

Loop commands until they behave.

InstallationQuick StartUsageAPI


A minimal CLI tool to execute commands in a loop with smart stop conditions. Perfect for polling, retrying, and automation tasks.

Installation

pip install -e .

Quick Start

# 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 5

Usage

Command Syntax

luper -c <command> [-c <command>...] [options]

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

Examples

Wait for a database to come online:

luper -c "pg_isready -h localhost" --until-success -d 1

Retry a flaky deploy:

luper -c "./deploy.sh" --until-success -n 3

Monitor a log file every 5 seconds:

luper -c "tail -1 /var/log/app.log" --forever -d 5

Run a build + test pipeline in a loop:

luper -c "make build" -c "make test" -n 10

Signal Handling

Signal Behavior
Ctrl+C Graceful stop after current iteration
Ctrl+C ×2 Force immediate exit

API

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}")

Result Objects

# 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 == 0

License

MIT


Dedicated to Lupe Quesada

About

Loop commands until they behave.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages