mirror of
https://github.com/kennethreitz/kennethreitz.org.git
synced 2026-06-05 22:50:17 +00:00
9e7d363648
New essay on physical modeling synthesis in pure Python — Karplus-Strong strings, tabla strokes, djembe cross-choking, Hammond organ drawbars. Software pages: - Responder: mention this site runs on it, link to colophon - All pages: change pip install to uv add Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
3.0 KiB
Markdown
94 lines
3.0 KiB
Markdown
# Delegator: Subprocesses for Humans
|
|
|
|
Delegator is a Python library for running shell commands without fighting `subprocess`. One function call. Real piping. Sane defaults.
|
|
|
|
$ uv add delegator.py
|
|
|
|
## What It Looks Like
|
|
|
|
```python
|
|
import delegator
|
|
|
|
# Run a command.
|
|
c = delegator.run("ls -la")
|
|
print(c.out)
|
|
print(c.return_code)
|
|
# 0
|
|
|
|
# Pipe commands together.
|
|
c = delegator.chain("ps aux | grep python")
|
|
print(c.out)
|
|
|
|
# Check for errors.
|
|
c = delegator.run("cat nonexistent.txt")
|
|
print(c.err)
|
|
# cat: nonexistent.txt: No such file or directory
|
|
|
|
# Run with a timeout.
|
|
c = delegator.run("sleep 100", timeout=5)
|
|
|
|
# Block until a command finishes, or don't.
|
|
c = delegator.run("long-running-task", block=False)
|
|
# ... do other work ...
|
|
c.block() # Wait when you're ready.
|
|
print(c.out)
|
|
```
|
|
|
|
No `subprocess.Popen` arguments to look up. No `shell=True` debates. No manual pipe wiring. Just run the command and get the result.
|
|
|
|
## The Problem
|
|
|
|
Python's `subprocess` module is one of the most powerful and most frustrating parts of the standard library. The number of arguments to `Popen` is staggering. The difference between `run`, `call`, `check_output`, and `Popen` trips up experienced developers. Piping two commands together requires more code than the commands themselves.
|
|
|
|
Here's what piping looks like with `subprocess`:
|
|
|
|
```python
|
|
import subprocess
|
|
|
|
p1 = subprocess.Popen(
|
|
["ps", "aux"],
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
p2 = subprocess.Popen(
|
|
["grep", "python"],
|
|
stdin=p1.stdout,
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
p1.stdout.close()
|
|
output = p2.communicate()[0]
|
|
```
|
|
|
|
And here's Delegator:
|
|
|
|
```python
|
|
c = delegator.chain("ps aux | grep python")
|
|
```
|
|
|
|
The difference isn't just fewer lines. It's that the Delegator version looks like what you're doing, while the subprocess version looks like an exercise in plumbing.
|
|
|
|
## The Story
|
|
|
|
Delegator started as a rewrite of my earlier library `envoy`. Same idea, better execution. It provides the two things you actually need: run a command, chain commands together. Everything else is handled with sensible defaults.
|
|
|
|
[Pipenv](/software/pipenv) uses Delegator internally for shell command execution. It's the kind of library that quietly makes other tools possible. Not everything needs to be a headline project. Sometimes the most useful thing you can build is a small, reliable utility that bigger projects can depend on without worry.
|
|
|
|
The project was gifted to [Amit Tripathi](https://github.com/amitt001), who now maintains it.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
$ uv add delegator.py
|
|
```
|
|
|
|
## Resources
|
|
|
|
- [Source Code on GitHub](https://github.com/amitt001/delegator.py)
|
|
- [Python Package Index](https://pypi.org/project/delegator.py/)
|
|
|
|
## Related
|
|
|
|
- [**Pipenv**](/software/pipenv) — Uses Delegator for shell command execution.
|
|
- [**Legit**](/software/legit) — Another tool for making command-line workflows more human.
|
|
- [**Requests**](/software/requests) — The "for humans" philosophy that inspired Delegator's design.
|
|
- [**Background**](/software/background) — Another small utility that does one thing well.
|