toolroom
Typed surfaces for command-line tools, generated from the tools themselves.
from toolroom import git, cmake, ruff
git.switch("-c", "release/v1.4")
cmake.build("build", parallel=8)
ruff.check("src", fix=True, select=["E", "F"])
A tool room is where precision tools are made, kept with their
measurements, repaired when they drift, and issued for use. That is the
whole design, one verb at a time: a stub generator reads each tool's own
--help and writes a typed surface for it; the surfaces are kept with
the version they were read from; a weekly refresh re-takes them when the
tool moves; and the bridge issues any tool as a Python handle —
including tools no stub has ever described.
toolroom.terraform("plan") runs whether or not toolroom has heard of
terraform.
Beta. toolroom is pre-1.0: minor versions may include breaking changes — always called out in the changelog, never in a patch release. Pin the minor (
toolroom~=0.6.0) if you build on it.
No transcription
toolroom does not transcribe a tool's flags into Python parameters. Transcription drifts: the wrapper pins the flag-set its author copied, the tool moves on, and one day a call emits a flag the installed binary rejects. Instead, keyword arguments translate mechanically, so the installed tool's own CLI stays the single source of truth, at whatever version it is:
fix=True→--fix(False/None→ omitted entirely)strict=off→--no-strict(offis thetoolroom.offsentinel)output_format="github"→--output-format githubselect=["E", "F"]→--select E --select Fx=1(single letter) →-x 1- a trailing underscore escapes Python keywords:
import_="x"→--import x
Attribute access chains subcommands
(toolroom.docker.compose.up(detach=True)), and positional strings pass
through verbatim. The typed stubs suggest — your editor knows the
flags and what each one does — but they never forbid: the hints decide
whether your editor can help, never whether a call works.
Using the tools has the
full grammar.
Standalone, or hosted
With nothing else installed, a call spawns the tool with plain
subprocess and answers in a Result — the exit code as an int
subclass, carrying the captured streams. A failure raises ToolError
unless the call opted into nofail.
When footman is present in the
process, the same call routes through footman's run() instead and
inherits everything a task run means: capture, replay-on-failure,
dry-run, recording(), --json receipts, parallel lanes. Neither
package imports or depends on the other — they were designed together
and are better together, but each is useful with the other absent.
With footman tells
that story.
Built, not run
.argv turns a handle's call into a command line instead of an
execution — an ordinary list[str], with .posix() and .windows()
for the moment it crosses into a shell:
cmd = git.push.argv(force=True) # ["git", "push", "--force"]
cmd.posix() # "git push --force", quoted for a POSIX shell
Testing
Code that calls tools is tested with toolroom.testing.answers(): the
handles stay real — chaining, flag translation, redaction are all
exercised — and only execution is replaced, answered from a table of
canned output and exit codes:
from toolroom.testing import answers
with answers({("git", "push"): 1}) as calls:
release() # exercises the failure path
assert calls[0].argv == ["git", "push"]
Testing has the whole story.
Install
uv add toolroom # or: pip install toolroom
toolroom has zero runtime dependencies and needs Python 3.11+. The docs live at willemkokke.github.io/toolroom. MIT licensed.
Metadata
Release files for toolroom 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| toolroom-0.6.1.tar.gz | 142.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| toolroom-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 312.5 kB
Release files / toolroom-0.6.1.tar.gz
| Download URL | toolroom-0.6.1.tar.gz |
|---|---|
| Size | 142.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
946b44a605e389ba4ddc61008fdc50b344f197a20d0ea146a16c7fe2bed20b4e
|
|
BLAKE2b-256 checksum How to use checksums |
8193bbcf50797c3e3f91d7b519b8833b1818b9a2cfe120f3ee9b1acb973cc749
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / toolroom-0.6.1-py3-none-any.whl
| Download URL | toolroom-0.6.1-py3-none-any.whl |
|---|---|
| Size | 170.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c4e0b09bc9c770d704542b901fc6c013deb671bf85e6fa91af6734c353ec687
|
|
BLAKE2b-256 checksum How to use checksums |
ca994a2fc57be0450973ff0e20ddef472e29cb7a13b645d2c2c45662908a837e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log