Abstractions for callback-heavy control flows.
Project description
Closive
Data Transformatics for Python
Closive is a focussed toolkit that elevates a chain of ordinary Python call-backs into an explicit, inspectable, serialisable pipeline.
Each step is a plain callable; every intermediate value is wrapped in a Success or Failure from returns.result; and the entire chain is a first-class object that can be traced, visualised, re-combined, or persisted.
A pipeline is to callbacks what a module is to functions: a durable unit of composition, documentation, and reuse.
Table of Contents
- Installation
- Quick Start
- Architecture & Core Abstractions
3.1 Closure Pipeline
3.2 Result Monad Integration
3.3 Commutator - Introspection & Debugging
- Error Handling Patterns
- Collection Utilities
- DataFrame & Plot Helpers
- External Pipelines (YAML)
- Extending Closive
- Contributing
- License
1 Installation
Closive is published on PyPI and supports Python 3.9 or later.
pip install closive
Optional extras for data-centric helpers:
pip install pandas numpy seaborn
2 Quick Start
from closive import closure, add, multiply, square
# 1. Construct a pipeline.
calc = (
closure(lambda x: x + 1) # seed
>> multiply(3) # step 1
>> square # step 2
>> add(5) # step 3
)
# 2. Execute like an ordinary function.
calc(2)
# → 50
# 3. Inspect the full run.
print(calc.explain(2))
Console output:
00 ✓ <lambda> → 3
01 ✓ multiply(3) → 9
02 ✓ square → 81
03 ✓ add(5) → 86
3 Architecture & Core Abstractions
3.1 Closure Pipeline
closure(fn=None, *, debug=False) → _Pipeline
- If
fnis omitted the pipeline begins with the identity step. - Append additional steps with the right-shift operator
>>. - The pipeline itself is callable; the first positional argument forms the initial value and any further
*args/**kwargsare forwarded to every step in the chain.
pipeline = closure() >> square >> add(7)
result = pipeline(4) # → 23
3.2 Result Monad Integration
Internal execution is performed in terms of returns.result.Result:
Success(value)represents normal flow.Failure(exc)represents an exception captured as data.
Closive automatically:
- Wraps raw outputs into
Success. - Short-circuits on the first
Failurenot otherwise handled. - Provides functional helpers (
map,bind,alt,lash) for direct Result manipulation.
3.3 Commutator
A commutator is a pass-through helper that performs a side effect yet returns the input value unchanged.
Closive ships several ready-made instances.
from closive import tap, log, identity, noop
peek = tap(print) # send value to stdout
audit = log() # log at INFO
A commutator is inserted exactly like any other step:
pipeline = closure() >> tap(lambda x: print("•", x)) >> square
Provided commutators:
| Name | Purpose |
|---|---|
identity |
Return the value unchanged. |
noop |
Produce None irrespective of input. |
tap(fn) |
Execute fn(value), then forward. |
log(...) |
Log the value, then forward. |
as_tuple |
Return (x, x). |
ignore_return |
Synonym for identity. |
4 Introspection & Debugging
Closive treats the pipeline as data and therefore supplies multiple inspection surfaces.
| Method | Result & Notes |
|---|---|
visualize() |
ASCII diagram of the pipeline topology. |
trace(value) |
Generator of per-step event dictionaries. |
explain(value, *, show_values=True) |
Pretty, colourised, step-by-step report, also renders rich HTML in Jupyter. |
inspect(value) |
JSON-serialisable dict containing input, output, per-step success, and metadata. |
get_step_result(value, index) |
Execute up to index and return the intermediate result, raising on Failure. |
_Pipeline(debug=True) |
Immediate print of step names during execution. |
5 Error Handling Patterns
Closive treats exceptions as values and lets you declare remediation strategies.
5.1 Static Fallback
safe = closure() >> divide(0) | 0 # ‘0’ returned on ZeroDivisionError
Operator | substitutes a static value and terminates the chain.
5.2 Dynamic Handler
def substitute(exc):
return f"handled: {exc}"
recover = (
closure()
>> risky_op
.handle_error(
handler=substitute,
continue_pipeline=True, # execute remaining steps
preserve_context=False # ignore original exception afterwards
)
)
handle_error parameters:
| Name | Type | Meaning |
|---|---|---|
fallback |
Any |
Static replacement value. |
handler |
Callable[[Exception], Any] |
Dynamic replacement; takes precedence over fallback. |
continue_pipeline |
bool |
If True, subsequent steps continue after replacement. |
preserve_context |
bool |
If True, replacement value returned as (value, exc). |
for_errors |
Sequence[type[Exception]] |
Only handle listed error types. |
propagate_others |
bool |
Re-raise non-listed exceptions when for_errors is used. |
6 Collection Utilities
The pipeline offers declarative operators that expect an iterable at runtime.
from closive import closure
stats = (
closure()
.map(lambda x: x ** 2) # element-wise
.filter(lambda x: x % 2 == 0) # keep even
.fold(lambda acc, x: acc + x, 0) # reduce to sum
)
stats([1, 2, 3, 4, 5]) # → 20
Additional structural helpers:
repeat(n)– append the last step n additional times.drain()– remove the last step.
7 DataFrame & Plot Helpers
If pandas, numpy, and seaborn are present, Closive exposes helpers that turn numerical pipelines into tabular and graphical artefacts.
7.1 Linear Function & Plot
from closive import closure, linfunc, linplot
params = ([0, 1, 2, 3, 4], 2, 1) # x-values, slope m, intercept b
p = closure(linfunc) >> linplot
plot = p(params) # seaborn.objects.Plot
plot.show()
7.2 Generic to DataFrame / Plot
from closive import closure, to_dataframe, to_plot
pipeline = closure() >> square >> to_dataframe >> to_plot
result, seaborn_plot, df = pipeline([0, 1, 2])
Return value is a triple so you can access whichever artefact you need.
8 External Pipelines (YAML Configuration)
Closive can materialise pipelines described in a YAML file at import time.
- Path:
~/.local/share/closive/pipelines.yml - Each top-level key becomes an attribute on the imported
closivemodule. - The file is auto-generated with an illustrative example on first run.
Example entry:
square_plus_five:
description: Square input then add five
steps:
- {module: closive.closures, function: square}
- {module: closive.closures, function: add, args: [5]}
Programmatic helpers:
from closive import save_pipeline, reload_pipelines
save_pipeline("square_plus_five", pipeline, "Reusable default chain")
reload_pipelines() # dynamically adds closive.square_plus_five
9 Extending Closive
9.1 Custom Result-Aware Step
from closive.closures import expects
from returns.result import Success, Failure
@expects
def capped(max_value):
def inner(result, *a, **k):
return result.bind(
lambda x: Success(x) if x <= max_value
else Failure(ValueError(f"{x} exceeds {max_value}"))
)
inner.__name__ = f"capped({max_value})"
return inner
Usage:
pipeline = closure() >> capped(10)
9.2 Augmenting _Pipeline
Because _Pipeline is an ordinary Python class, project-specific conveniences can be added dynamically.
def normalise(self):
return self.map(lambda v: (v - min(v)) / (max(v) - min(v)))
from closive.closures import _Pipeline
_Pipeline.normalise = normalise
10 Contributing
Bug reports, feature proposals, and pull requests are welcome.
git clone https://github.com/kosmolebryce/closive
cd closive
pip install -r dev-requirements.txt
pytest
Contribution guidelines:
- Follow [Google Python Style Guide].
- Public APIs must include docstrings and unit tests.
- Commit messages should be imperative and scoped, e.g. “Add trace summariser”.
11 License
Closive is distributed under the MIT License.
See the LICENSE file for the full legal text.
© 2025 K. LeBryce. All rights reserved.
Project details
Release history Release notifications | RSS feed
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 closive-0.0.0.dev26.tar.gz.
File metadata
- Download URL: closive-0.0.0.dev26.tar.gz
- Upload date:
- Size: 84.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f42b4ee0983c2a8529962392b9c2be169d9539bf668d677d4f8ce4cc37115e5e
|
|
| MD5 |
6530f7de0a860c23128c2c8eb3be3566
|
|
| BLAKE2b-256 |
9947a8644920d9bf4c828df7ec5a8e51754c5aa8472ce84f7517177349e959ab
|
File details
Details for the file closive-0.0.0.dev26-py3-none-any.whl.
File metadata
- Download URL: closive-0.0.0.dev26-py3-none-any.whl
- Upload date:
- Size: 26.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a64c55ee0d782e124d236c943371ab4f7dba28214a3601a19037b5062669979
|
|
| MD5 |
a853ba51c7670a6e9a5f344a81ac2d3b
|
|
| BLAKE2b-256 |
25bd16efe921fcec728f448c08cc3b0bcc133ddc3724fd9ef101a9966b86600f
|