# Plumbum's own readme directory listing predates the repository

> Plumbum gives Python the pipe, the redirect and the background job, plus remote execution over SSH and a toolkit for building command line applications. It also has the smallest install-time dependency list of anything in this category: one conditional typing backport. The readme, though, has not been run in a long time. Its first example prints a directory listing containing four files that no longer exist, and its next two examples read one of them.

**tomerfiliba/plumbum** — Plumbum: Shell Combinators

- Repository: https://github.com/tomerfiliba/plumbum
- Website: https://plumbum.readthedocs.io
- Stars: 3,057 · Forks: 209
- Language: Python
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/tomerfiliba-plumbum

## The first example prints a tree that has been deleted

The readme opens with a teaser and its first line runs the listing command and prints the result.

The output is a directory listing containing a build script, a packaging configuration file, a dependency manifest for a tool the project no longer uses, an egg-info directory, a test configuration file, a setup script and a setup configuration file.

None of those seven is in the repository now. What is there instead is a single project configuration file, a test automation file, an editor configuration for the assistant tooling, and a pre-commit configuration. The change of packaging system is visible in the difference between those two lists.

So the first thing a new reader sees is a program printing a directory they do not have.

That is only cosmetic in isolation. What makes it worth pointing out is the pattern: this is a readme whose examples have not been executed since the packaging migration, and the failure mode is silent. Nothing raises an error, the API shown is still correct, and only the file names inside the printed output are wrong.

## Two more examples read the file that is missing

The piping example filters the listing for Python files and gets three back: the build script, the setup script and the translations module.

The translations module is still there. The other two are not.

The redirection example goes further. It concatenates the setup script into a filter that prints the first four lines, and the expected output is a shebang line, an import of the standard library operating system module, two blank lines and the start of a try block. Every one of those characters came out of a file the repository deleted.

There is a third consequence further down. The working-directory example re-runs that same filter after changing into a subdirectory, and the expected line count it prints is the count of remaining Python files there.

So three separate examples in the readme are calibrated against a tree that has since been reorganised, and all three still look plausible. A reader who follows the redirection example against the current repository gets an error rather than a wrong answer, which is the best of the three outcomes, but they get it on the second code block of the page.

## The whole install-time dependency list is one typing backport

The dependency declaration for a library that runs subprocesses, opens SSH connections, resolves paths, formats terminal colour and builds command line applications is this:

```
typing_extensions; python_version<'3.13'
```

One entry, and it is conditional on the interpreter version. Above 3.13 there is nothing to install alongside the package at all.

Getting there took work. Remote execution by default shells out to a real SSH client rather than implementing the protocol, which is why a pure-Python SSH implementation is an optional extra rather than a dependency. Path handling is done with the standard library's path types. Colour is written to the standard streams rather than through a terminal library. And the command line toolkit is built on the standard library's argument parser rather than on a framework.

The classifiers match that discipline: typed, Windows and POSIX, and six consecutive Python versions from 3.9 through 3.15.

So the cheapest install in this batch is also the one with the most surface, and the trade is documented rather than hidden.

## Remote execution needs an extra the readme does not mention

Remote commands are one of the four capabilities the readme lists, and the SSH section says it supports three things: any OpenSSH-compatible client, the Windows SSH client, and a pure-Python implementation of the protocol.

The pure-Python one is an optional extra:

```
ssh = [
    "paramiko",
]
```

So the first two backends need nothing installed, because they are invoked as programs, and the third needs an extra that the readme never mentions.

The API is the same either way, and the example constructs a remote machine with a host, a user and a key file path, then indexes it for a command, and uses a context manager to change its working directory before piping a filter over the result.

For anyone whose environment already has a system SSH client, which is most of them, this is invisible. For anyone on Windows without one, or on a locked-down host where shelling out is not allowed, the extra is the difference between the headline feature working and not.

## Pipes do no shell parsing, so a pattern stays literal

The piping example is four lines and the third one is the one worth understanding.

You build a chain from a listing command, a filter and a line counter, passing the filter a negated match option and a pattern. Printing the chain gives you a single string: the three absolute paths joined by pipes, with the pattern quoted exactly as you wrote it.

The pattern is a raw string containing an escaped dot, and it appears in the printed representation as written, not as something a shell would have rewritten. That is the design decision in one line: these are objects, not text. A pipe is a composition of command objects, so there is no parsing layer, no quoting rules to learn, and no difference between what you print and what runs.

It also explains why the example works identically on Windows, which is the cross-platform claim in the readme's opening paragraph. There is no shell to differ.

The cost is the thing a shell gives you for free. You cannot write a command line as a string and hand it over; every argument is an explicit element of the composition.

## Two colour APIs, one of them called unsafe

The last section of the readme is terminal colour, and it deliberately offers two ways in.

One is a context manager for a named colour, and a bitwise operator that composes a style with a string. The other is the same shape with named colours drawn from a larger palette, an operator combining a foreground style with an underline, and a helper taking three numbers for a full colour.

And then the line that explains the two APIs: unsafe colour access is available too, written as string concatenation with a background colour and an explicit reset.

So the library ships a safe path that tracks state for you and an unsafe path that does not, with a reset you have to remember. That is an unusual thing to document and a sensible thing to offer, since the safe path costs a little and the fast path does not.

The rest of the cheat sheet is in the same register. Foreground and background execution are one operator applied to a chain, with a marker for printing directly and a marker for a future object. Command nesting is indexing one command with another, which resolves to fully qualified paths on both sides.

## The name is lead, and the searches are all chemistry

The readme explains the etymology in its second paragraph: the name is Latin for lead, and lead was used to make pipes.

That sentence exists because of what happens when you search for the name. The related terms and the questions attached to this repository are almost entirely about the chemical element, its symbol, its pronunciation, its homeopathic uses, and whether the element and its Latin name are the same thing.

The project competes, in search results, with a metal and with a pharmacy.

The readme does not try to fight that, beyond the etymology. What it does instead is put the project page in the metadata as a documentation site rather than as the repository, and add a cheatsheet link, a changelog link and a bug tracker link to the package metadata so that the package index has somewhere to send people who found the wrong plumbum.

That is the right mitigation and it is worth naming, because for a library with a name that is also a dictionary word and an element symbol, discoverability is a maintenance cost rather than a marketing detail.

## Conclusion

Plumbum fits Python code that has to run a command, capture its output, or run it on another machine, without a shell string and without a shell to debug. Two things to check before you build on it. Read the installed documentation rather than the repository readme, because the examples there have drifted far enough that two of them reference files the project deleted years ago, and the prose you will actually learn from is on the documentation site. And decide what you need from SSH before you install, because remote execution is advertised in the same breath as everything else while one of its three backends is behind an install extra that the readme never mentions.

## FAQ

### what is plumbum

Two different things share the name. The common one is the Latin word for lead, and the search traffic attached to this repository is mostly chemistry, pronunciation and homeopathy. This repository is a Python library that gives shell-style pipes, redirection, background execution and remote commands to Python, plus a toolkit for building command line applications.

### What does plumbum mean in Latin?

Lead. The readme says so and adds the reason it chose the name: lead was used to make pipes back in the day, which is also where the word for the pipe itself comes from.

### is plumbum and lead the same

As a chemical element, yes, and nothing in this repository is about chemistry. As a Python library the name refers to the pipe: the library composes command objects with a pipe operator rather than running a shell, which is also why the readme claims to be cross-platform with no shell involved.

### Does plumbum support SSH without installing anything?

For the two backends that shell out to an SSH client, yes, including the Windows client. The third supported backend is a pure-Python implementation of the protocol and it sits behind an optional extra, which the readme's SSH section does not mention.

### What are plumbum's dependencies?

One, and only on older interpreters: a typing backport declared conditionally for anything below a recent Python version. Above that floor the package installs nothing alongside itself, and the classifiers claim six consecutive Python versions and a typed interface.

## Sources

- [License: MIT](https://github.com/tomerfiliba/plumbum/blob/master/LICENSE)
- [Project website](https://plumbum.readthedocs.io)
- [README](https://github.com/tomerfiliba/plumbum/blob/master/README.md)
- [Releases](https://github.com/tomerfiliba/plumbum/releases)
- [tomerfiliba/plumbum on GitHub](https://github.com/tomerfiliba/plumbum)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tomerfiliba-plumbum
