A plumbing syntax for Python
Project description
plumbum
A plumbing syntax for Python that provides a clear distinction between data and operators, enabling composable function pipelines.
Overview
plumbum is a library for threading data through function calls using intuitive pipe operators. Inspired by Pipe, it offers a redesigned approach with a separation between operator construction and execution.
This is primarily a syntax library focused on making data transformations more readable and composable. It is not optimized for performance-critical applications, but rather caters to the aesthetics of @habemus-papadum -- so, your mileage may vary...
Installation
pip install habemus-papadum-plumbum
Usage
# Basics
5 > add(1) | mul(2) # 12
# Iterators
[1, 2, 3] > select(mul(3) | add(1)) | where(even) | list # [4, 10]
# Async
await (5 > async_add(1) | async_mul(2)) # 12
await ([1, 2, 3] > aiter | aselect(async_add(1)) | alist) # [2, 3, 4]
# Pipelines are reusable values
op = add(1) | multiply(2)
op2 = add(3) | multiply(12)
6 > op | op2 # 204
Mental Model
- Compose first, execute later. The
|operator chains together@pb/@apboperators (and plain callables) without running them. The pipeline becomes a first-class value that you can store, combine, or pass around. - Thread data explicitly. The
>operator injects the left-hand value into the pipeline’s first argument. That works for numbers, strings, dicts, iterables, custom classes—any Python object. - Async is contagious by design. Introducing an async operator (created with
@apb, an async iterator helper, or any awaitable) upgrades the whole pipeline sovalue > pipelinereturns a coroutine. Call it withawait. - Iterables stay lazy. Synchronous helpers like
select,where,take, orchainreturn iterators; async counterparts (aselect,awhere,aiter, …) yield async iterators. Add a materializer (list,tuple,alist) when you actually need the concrete collection.
Features
- Compatible with arbitrary data types; the library focuses on syntax and composability rather than constraining what flows through the pipes.
- Seamless sync/async mixing: pipelines promote themselves to async when an async stage is present, including adapters for awaitables returned by sync operators.
- Rich iterable toolkits for sync (
pdum.plumbum.iterops) and async (pdum.plumbum.aiterops) flows, covering batching, zipping, traversal, networking, and more.(Modelled after the operators fromPipe) - jq-inspired JSON utilities (
pdum.plumbum.jq) for parsing dotted paths, querying nested data, and performing immutable transformations—plus async counterparts.
Style Guide
- Favor
|for composition. Build operators with|so you can refactor pipelines into reusable pieces. Use>sparingly—ideally once—when you finally thread data through the pipeline. Remember that|binds more tightly than>, sovalue > a | bmeansvalue > (a | b); add parentheses only when you truly need different grouping. - Iterators remain lazy by default. Iterator pipelines usually yield another iterator. In tests or scripts, append a materializer such as
| listwhen you need concrete values. Plain callables are auto-wrapped; no need forpb(list). - Sync to async promotion is automatic. Pipelines built from
@pboperators seamlessly adopt async semantics when an@apbstage (or any awaitable) appears. Keep composing with|; the result becomes awaitable. - Await async chains. Any pipeline that includes async operators returns a coroutine. Execute it with
await (value > pipeline)(or equivalentlyawait pipeline(value)). - Collect async results with
alist/acollect. Finish async iterator pipelines with| alist(alias| acollect) when you need a list in memory. - Avoid chained
>comparisons. Python treatsx > a > bas a chained comparison, which is incompatible with plumbum operators. Preferx > a | b; only use(x > a) > bwhen you intentionally need two separate execution steps.
Tutorial Notebook
Learn the details of synchronous and asynchronous pipelines by following along step-by-step in the Tutorial, which hosts the full quick-start tour covering core operators, partial application, iterable helpers, and async variants.
Acknowledgements
- plumbum draws direct inspiration from Julien Palard’s Pipe; several iterable operators (for example
select,where, anddedup) started life there before being reworked for plumbum’s APIs. - Key differences from Pipe:
- Operators are inert values:
|builds pipelines while>triggers execution, making composition and reuse explicit. - Async is a core capability: sync and async operators coexist in the same pipeline, and iterator helpers come in both synchronous and asynchronous flavors.
- The bundled
pdum.plumbum.jqmodule adds a jq-inspired path/query layer, expanding the original operator toolbox into structured-data transformations.
- Operators are inert values:
- The codebase was authored primarily with AI assistance—roughly 95% by OpenAI Codex and 5% by Claude Code—with artistic direction and stewardship by @habemus-papadum.
Development
This project uses UV for dependency management.
Setup
# Install UV if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone the repository
git clone https://github.com/habemus-papadum/pdum_plumbum.git
cd pdum_plumbum
# Provision the entire toolchain (uv sync, pnpm install, widget build, pre-commit hooks)
./scripts/setup.sh
Important for Development:
./scripts/setup.shis idempotent—rerun it after pulling dependency changes- Use
uv sync --frozento ensure the lockfile is respected when installing Python deps
Running Tests
# Run all tests
uv run pytest
# Run a specific test file
uv run pytest tests/test_example.py
# Run a specific test function
uv run pytest tests/test_example.py::test_version
# Run tests with coverage
uv run pytest --cov=src/pdum/plumbum --cov-report=xml --cov-report=term
Code Quality
# Check code with ruff
uv run ruff check .
# Format code with ruff
uv run ruff format .
# Fix auto-fixable issues
uv run ruff check --fix .
Building
# Build Python
./scripts/build.sh
# Or build just the Python distribution artifacts
uv build
Publishing
# Build and publish to PyPI (requires credentials)
./scripts/publish.sh
Automation scripts
./scripts/setup.sh– bootstrap uv, pnpm, widget bundle, and pre-commit hooks./scripts/build.sh– reproduce the release build locally./scripts/pre-release.sh– run the full battery of quality checks./scripts/release.sh– orchestrate the release (creates tags, publishes to PyPI/GitHub)./scripts/test_notebooks.sh– execute demo notebooks (uses./scripts/nb.shunder the hood)./scripts/setup-visual-tests.sh– install Playwright browsers for visual tests
License
MIT License - see LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file habemus_papadum_plumbum-0.6.0.tar.gz.
File metadata
- Download URL: habemus_papadum_plumbum-0.6.0.tar.gz
- Upload date:
- Size: 160.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6289f2bee467b5f1b51be078ae02a5a47c3c0a4f6e63f497af50ef3139dfa266
|
|
| MD5 |
9d2de0f1ad82b92d4b18616406294917
|
|
| BLAKE2b-256 |
3c43b7e7acb6669c74d2cfd46d0220e600f4fb1a56e91394c1ce80288a4a9cf2
|
File details
Details for the file habemus_papadum_plumbum-0.6.0-py3-none-any.whl.
File metadata
- Download URL: habemus_papadum_plumbum-0.6.0-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-httpx/0.28.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23f6e9b708f898e69c6b0a4d50ba5a93b24321d23568ad3309e85dea62b590d0
|
|
| MD5 |
c3b8daf6a0b12f6b1fc5c90b57f6df97
|
|
| BLAKE2b-256 |
9fdc1dfc26bbd7c59bafab5c0e42e932aed442ba76b656bfef2eb78a5077feee
|