Skip to main content

Lightweight command timeout utility with zero runtime dependencies

Project description

timeout-dead

PyPI version Python License Tests Ruff Pyright

Lightweight command timeout utility with zero runtime dependencies. Runs any shell command with a configurable time limit and termination signal. If the command exceeds the timeout, timeout-dead sends the chosen signal, waits a 1-second grace period, then force-kills the process. Works on Linux, macOS, and Windows (Git Bash / WSL).

Installation

pip install timeout-dead

Or via uv:

uv tool install timeout-dead

Requires Python 3.10 or later. Zero runtime dependencies — pure Python standard library.

Quick start

# Run a command with default 60s timeout
timeout-dead "python -c 'print(42)'"

# Short alias also works
time-d "echo hello"

# Specify a custom timeout
timeout-dead --sec 120 "npm run build"

# Sub-second timeouts work too
timeout-dead --sec 0.5 "potentially-hanging-tool"

# Use SIGINT instead of default SIGTERM
timeout-dead --signal INT --sec 30 "long-running-server"

# Run silently — suppress all normal output
timeout-dead --no-output "curl -s https://example.com"

Usage

usage: timeout-dead [-h] [-v] [--sec SECONDS] [--signal SIGNAL] [--no-output] COMMAND ...

Lightweight command timeout utility.

positional arguments:
  COMMAND               command to execute

options:
  -h, --help            show this help message and exit
  -v, --version         show version and exit
  --sec SECONDS         timeout in seconds (default: 60.0, accepts floats)
  --signal SIGNAL       signal to send on timeout (TERM, KILL, HUP, INT)
  --no-output           suppress normal output (stdout, stderr, header, footer)

How it works

  1. timeout-dead starts the command in a new process group (Unix) / console group (Windows).
  2. A background timer waits for the specified timeout.
  3. If the command finishes in time, its output and exit code are forwarded.
  4. If the timeout expires:
    • The chosen signal is sent to the process group.
    • After 1 second, if the process is still running, SIGKILL (Unix) or process.kill() (Windows) is sent.
    • A Timeout exceeded message is printed to stderr.

Signal reference

Signal Unix Windows
TERM SIGTERM (15) — terminate gracefully CTRL_BREAK_EVENT — console break
KILL SIGKILL (9) — force kill Falls back to TerminateProcess
HUP SIGHUP (1) — hangup Falls back to TerminateProcess
INT SIGINT (2) — interrupt (Ctrl+C) CTRL_C_EVENT — console interrupt

Why subprocess timeout is not enough

Python's built-in subprocess timeout only kills the direct child, not its entire process tree. If your command spawns subprocesses (npm install, make, docker build), children survive the parent kill.

timeout-dead uses process groups to terminate everything — every subprocess, pipeline, and child.

Real-world scenarios

Scenario Command Why it hangs timeout-dead
Gradle build ./gradlew build 20+ min fresh build, agent generates duplicate commands timeout-dead --sec 600 "./gradlew build"
CMake build cmake --build . Locks waiting for dependency resolution timeout-dead --sec 180 "cmake --build ."
Spring Boot ./gradlew bootRun Server never exits, agent won't proceed timeout-dead --sec 30 --signal INT "./gradlew bootRun"
Docker build docker build -t myapp . Network timeout, internal retries, no progress timeout-dead --sec 600 "docker build -t myapp ."
npm install npm install Corrupted cache or registry auth hang timeout-dead --sec 300 "npm install"
Interactive REPL python / node / irb Waits for input, agent doesn't know timeout-dead --sec 5 "python"

For AI agents

If you build agents that execute shell commands, timeout-dead is essential infrastructure. Agents frequently generate commands that hang — waiting for input, entering infinite loops, or starting interactive programs.

Instead of agents freezing indefinitely, wrap every command:

timeout-dead --sec <timeout> --signal <signal> "<command>"

No dependencies, no code changes. Agent always gets a response — exit code + output — and can implement retry, fallback, or user notification.

Development

git clone https://github.com/UmbrellaLeaf5/timeout-dead
cd timeout-dead
uv sync --extra dev
uv run pytest tests/ -v
uv run ruff check src/timeout_dead/ tests/
uv run ruff format --check .
uv run pyright src/timeout_dead/

License

Unlicense — public domain.

Timeout icons created by Those Icons - Flaticon

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

timeout_dead-0.2.0.tar.gz (71.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

timeout_dead-0.2.0-py3-none-any.whl (8.0 kB view details)

Uploaded Python 3

File details

Details for the file timeout_dead-0.2.0.tar.gz.

File metadata

  • Download URL: timeout_dead-0.2.0.tar.gz
  • Upload date:
  • Size: 71.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for timeout_dead-0.2.0.tar.gz
Algorithm Hash digest
SHA256 54438361f963b216dc2f07c40493bef5f73d76f34a9c5fc7f826508e169a2e34
MD5 e15342999499a705882c3a0886cbf2e1
BLAKE2b-256 a1c70a6bdd83c2590e2ca6422a637baf27a2736423479fe4e4bca95d5dc16840

See more details on using hashes here.

Provenance

The following attestation bundles were made for timeout_dead-0.2.0.tar.gz:

Publisher: publish.yml on UmbrellaLeaf5/timeout-dead

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file timeout_dead-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: timeout_dead-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 8.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for timeout_dead-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1aadd5554daa021feee7901024f7da11df594588cc82c405bb13603987db3863
MD5 f5d847ec475d731460682f772db647c6
BLAKE2b-256 29d87366983c71633b585667aadb9ea9d9199e84fadec4f8437fa05ad0a74cfc

See more details on using hashes here.

Provenance

The following attestation bundles were made for timeout_dead-0.2.0-py3-none-any.whl:

Publisher: publish.yml on UmbrellaLeaf5/timeout-dead

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page