Skip to content
Command runner with clean API.
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.


Type Name Latest commit message Commit time
Failed to load latest commit information.


Commandlib is a dependencyless library for calling external UNIX commands (e.g. in build scripts) in a clean, readable way.

Using method chaining, you can build up Command objects that run in a specific directory, with specified environment variables and PATHs, etc.

For simplicity's sake, the library itself only runs commands in a blocking way (all commands run to completion before continuing), although it contains hooks to run non-blocking via either icommandlib or pexpect.

Pretend 'django/':

# Pretend django "" that just prints out arguments:
import sys ; sys.stdout.write(' '.join(sys.argv[1:]))
from commandlib import Command

# Create base command
python = Command("python")

# Create command "python" that runs in the django directory
manage = python("").in_dir("django")

# Build even more specific command
dev_manage = manage.with_trailing_args("--settings", "")
# Run combined command
dev_manage("runserver", "8080").run()

Will output:

runserver 8080 --settings


$ pip install commandlib



Commandlib avoids the tangle of messy code that you would get using the subprocess library directly (Popen, call, check_output(), .communicate(), etc.) and the confusion that results.

It's a heavily dogfooded library.

Why not use Delegator instead (Kenneth Reitz's 'subprocesses for humans')?

Kenneth Reitz (author of requests "urllib2/3 for humans"), wrote a similarly inspired "subprocess for humans" called envoy. That is now deprecated and there is now a replacement called delegator, which is a very thin wrapper around subprocess.

Features delegator has which commandlib does not:

  • Delegator can chain commands, much like bash does (delegator.chain('fortune | cowsay')). Commandlib doesn't do that because while dogfooding the library I never encountered a use case where I found this to be necessary. You can, however, easily get the output of one command using .output() as a string and feed it into another using piped.from_string(string).

  • Delegator runs subprocesses in both a blocking and nonblocking way (using pexpect). commandlib only does blocking by itself but if you pip install pexpect or icommandlib it can run via either one of them.

Features which both have:

  • Ability to set environment variables.
  • Ability to run pexpect process from command object.

Features which only commandlib has:

  • Ability to set PATH easily.
  • Ability call code from within the current virtualenv easily.
  • Ability to pipe in strings or files and easily pipe out to strings or file (or file handles).

Why not use other tools?

  • subprocess - if you just call one or two external commands, it's probably fine, but beyond that it will become a mess.

  • os.system(*) - only capable of running simple bash commands.

  • sh - uses a lot of magic. Attempts to make python more like shell rather than making running commands more pythonic.

  • plumbum - similar to amoffat's sh, tries to make a sort of "bash inside python". Also has a weird way of building commands from dict syntax (grep["-v", "\.py"]).

You can’t perform that action at this time.