Skip to main content

Zap

Python subprocess, but actually nice.

PyPI Downloads Python License Zero Dependencies


Before:

result = subprocess.run(["git", "status"], capture_output=True, text=True, check=True)
print(result.stdout)

After:

from zap import run
print(run("git status"))

Install

pip install zap-sh

Quick Start

from zap import run

# Run a command — returns a Result object
result = run("git status")
print(result.stdout)   # raw stdout
print(result.ok)       # True if exit code 0
print(result.code)     # exit code
print(result.lines)    # stdout as list of lines

# Just print it — str() returns stdout
print(run("ls -la"))

Pipe Chaining

# Pipe with strings — clean and simple
result = run("cat logs.txt") | "grep ERROR" | "wc -l"
print(result)  # number of error lines

Live Output

See output in real-time while still capturing it. No more staring at a blank screen during pip install or docker build.

# Stream output live to terminal — still captured in result
result = run("pip install flask", live=True)

# Works with long builds too
run("docker build -t myapp .", live=True)
run("make all", live=True)

Retry

Automatically retry flaky commands — network calls, API requests, CI scripts.

# Retry up to 3 times with 2s delay between attempts
result = run("curl -f https://api.example.com/health", retries=3, delay=2)

# Only retries on failure (non-zero exit) — succeeds immediately if ok
run("flaky-deploy-script.sh", retries=5, delay=5)

Check If a Command Exists

from zap import which

# Returns the path if found, None if not
if which("docker"):
    run("docker ps")
else:
    print("Docker not installed")

# Great for setup scripts
for cmd in ["git", "node", "python3"]:
    path = which(cmd)
    print(f"{cmd}: {path or 'NOT FOUND'}")

Temporary Directory

from zap import cd

# Change directory temporarily — auto-restores when done
with cd("/my/project"):
    run("git status")
    run("npm install")
    run("npm run build")
# Back to original directory here

# Safe even if something errors
with cd("/tmp"):
    run("some-command")  # if this fails, directory still restores

Error Handling

from zap import run, ZapError

# Raises ZapError on non-zero exit (default)
try:
    run("git push origin fake-branch")
except ZapError as e:
    print(e.stderr)
    print(e.code)

# Disable with check=False
result = run("might-fail", check=False)
if not result.ok:
    print("Failed:", result.stderr)

Options

# Timeout (seconds)
run("sleep 100", timeout=5)  # raises TimeoutError

# Working directory
run("ls", cwd="/tmp")

# Environment variables (merged with os.environ)
run("echo $API_KEY", env={"API_KEY": "secret"})

# Feed stdin
run("cat", stdin="hello from stdin")

# Safe mode — pass a list instead of string
run(["echo", "no shell injection"])

Async

from zap import run_async

result = await run_async("git status")
print(result.ok)

Same API as run() — just await it.

Result Object

Property Type Description
.stdout str Captured stdout
.stderr str Captured stderr
.code int Exit code
.ok bool True if exit code is 0
.lines list[str] Stdout split into lines
.json() Any Parse stdout as JSON
str(r) str Returns stdout stripped
bool(r) bool Returns .ok
r | "cmd" Result Pipe stdout to next command

run() Parameters

Parameter Type Default Description
command str | list required Command to run
check bool True Raise ZapError on non-zero exit
timeout float None Timeout in seconds
cwd str None Working directory
env dict None Extra env vars (merged with os.environ)
stdin str None String to feed as stdin
live bool False Stream output in real-time
retries int 0 Number of retry attempts on failure
delay float 1.0 Seconds between retries

How It Works

  • String command → runs with shell=True (convenience for scripting)
  • List command → runs with shell=False (safe, no shell injection)
  • check=True by default → raises ZapError on non-zero exit
  • live=True → uses Popen with selectors for real-time streaming
  • retries → catches failures and retries with configurable delay
  • Zero dependencies → pure Python stdlib
  • ~300 lines → read the whole source in 5 minutes

Support

If Zap saves you time, consider buying me a coffee.

Buy Me A Coffee

License

MIT

Metadata

Release files for zap-sh 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zap-sh 0.2.0
File Size Uploaded Provenance Actions
zap_sh-0.2.0.tar.gz 10.8 kB Without provenance Details for zap_sh-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for zap-sh 0.2.0
File Interpreter ABI Platform Actions
zap_sh-0.2.0-py3-none-any.whl Python 3 none any Details for zap_sh-0.2.0-py3-none-any.whl

Total release size: 18.4 kB

Release files / zap_sh-0.2.0.tar.gz

Download URL zap_sh-0.2.0.tar.gz
Size 10.8 kB
Tags Source
SHA-256 checksum
How to use checksums
81644762b4a99d401448cb619628a6a05ad0ef9fab7186ca5ea97f3a147e7a33
BLAKE2b-256 checksum
How to use checksums
8bed8e05040859c7a9ccfc3b53b4c013c50605bac53511a577a793f24e7c1dbc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.7

Release files / zap_sh-0.2.0-py3-none-any.whl

Download URL zap_sh-0.2.0-py3-none-any.whl
Size 7.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
23b0bbc6de7676f8515c1943334dc3366adcccde063a30709ee77589c08410b1
BLAKE2b-256 checksum
How to use checksums
c899ecb0b2f6849ef5df3c82f6986e489382f364da387e54c4b797eadae276be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.12.7

Security for zap-sh 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page