Skip to main content

pyochain

pyochain is a python library that provides various classes with a fluent API, to work with iterations, collections, handle optional values, manage errors, and more!

Table of Contents


Key Features

  • Option[T] to handle optional values instead of T | None.
  • Result[T, E] to handle success and error paths instead of try.. except blocks.
  • Iterator types covering python builtins (zip, map, ...), itertools,many methods from Rust's Iterator, and libraries like toolz or more-itertools.
  • Collection types covering python builtins (list, deque, set, Counter, ...)
  • Reimplementation of heapq module with an OOP API, with HeapMin and HeapMax types.
  • No-copy views slices, with SliceView type, on any Sequence.
  • Sorted data structures (mappings, set, sequences), adapted from the sortedcontainers library.
  • ABC's hierarchy mimicking collections.abc, for duck typing, shared methods, and the possibility to implement your own subclasses.
  • Mixin's addable to any class, to provide a fluent API with pipe and tap, or Option/Result conversions on truthiness evaluation.

Core design principles

  • Compiled in Rust for maximum performance with Pyo3.
  • Fluent API design for chaining method calls, to read your code just like a book => from top to bottom, left to right.
  • First class static typing support: Generics, overloads, and pattern matching for Option and Result types.

That's why it's called pyochain: it allows you to build chains of operations on your data, with code compiled in Rust thanks to Pyo3.

Installation

Pyochain supports python 3.13 and above, and compile wheels for Linux, Windows and MacOS.

uv add pyochain # or pip install pyochain

Links

🐍Pypi package

📚 Full API Reference

📄 README.md

Why use pyochain?

🔥 Blazingly fast

Being statically compiled, pyochain is by design order of magnitude faster than other similar python libraries.

For example, a simple, single object creation like x = Vec(1, 2, 3) take 30% less time than if Vec was implemented in pure Python.

This speed-up is only exacerbated for Iterator methods and classes, often up to 2x to 10x faster than more-itertools equivalents.

Even when the source code was still mostly python, great care had been taken to optimize performance, which is probably why it was already ranked as the fastest library in its category in this comparison (at this point, only Result and Option were compiled, which were not relevant for this benchmark).

🛡️ 100% type-safe

IDE autocompletion is a primary concern, and pyochain brings exhaustive overloads and generics support for all its constructs.

It's even more complete than typeshed in certain cases, for example with map_star fully typed regarding arguments and return types, while itertools.starmap is not.

This is the hardest part to test when developing the library, so if you encounter any typing issues, please report them!

📚 Accurate, tested Documentation

Every method and type is exhaustively documented, and contains runnable examples that are tested for correctness.

Even this README is tested!


Getting started

Mixin's

Pipe, Tap, Checkable, and other mixins are simple mixins providing fluent API's for method chaining and Option/Result conversions.

They don't depend on internal state except __bool__, thus making them universally applicable to any subclass.n.

Users of pandas, polars or the Rust crate tap will feel right at home with pipe() and tap, while Rust developers will appreciate the Checkable type, providing methods like then, then_some, or ok_or_else, evaluating the instance truthiness to return corresponding Option or Result types, just like the bool methods in Rust.

All pyochain types herit from them, making control flow for collection emptyness or error handling a natural part of a pipeline.

Iterators

Pyochain has various Iterator types with a fluent API, whose functionnalities come from:

  • Python builtins (fully covered) => zip, map, ...
  • itertools module (fully covered) => chain, combinations, ...
  • Rust std::iter::Iterator => try_collect, partition, ...
  • more-itertools library => all_unique, arg_max, tail, ...
  • toolz library => map_juxt, count, first, ...

They can be used to build complex pipelines of transformations, filters, and aggregations in a readable way (no nested loops or comprehensions), without creating intermediate collections (lazy execution), thus improving performance and memory usage.

Many methods act just like their Rust counterparts: filter_map filter the Option returned by a closure, find return the first element matching a predicate wrapped in an Option, etc...

Below is an example of how to use Iter, the generic iterator type, and how it compares to a pure Python implementation using itertools:

from pyochain import Iter, Seq
import itertools

wanted = ((0, "1"), (1, "9"), (2, "25"), (3, "49"), (4, "81"))

pyochain_res = (
    Iter
    .from_count(1)
    .filter(lambda x: x % 2 != 0)
    .map(lambda x: x**2)
    .take(5)
    .enumerate()
    .map_star(lambda idx, value: (idx, str(value)))
    .collect(tuple)
)
py_res = tuple(
    itertools.islice(
        itertools.starmap(
            lambda idx, val: (idx, str(val)),
            enumerate(
                map(lambda x: x**2, filter(lambda x: x % 2 != 0, itertools.count(1)))
            ),
        ),
        5,
    )
)
assert pyochain_res == py_res == wanted

Collections

Each python built-in collection type (list, tuple, range, dict, set, etc...) has a corresponding pyochain type, with additional collections like SliceView (no copy view of a slice), or StableSet (a mutable set that preserves insertion order), and more planned for the future.

Many methods are designed to interoperate with the rest of the library: Dict::get_item or Seq::get return an Option, Vec::drain a PyoIterator, and so on.

from pyochain import Dict, Iter, Some
from pyochain.collections import StableSet

names = ["Charlie", "Alice", "Bob", "Alice"]


# Create a Dict from an iterator of key-value pairs
data = Iter.from_count().zip(names).collect(Dict)
assert data == Dict({0: "Charlie", 1: "Alice", 2: "Bob", 3: "Alice"})


# try_insert returns a Result, which is Err if the key already exists
err = data.try_insert(1, "David")
assert err.is_err()

# sort return a Vec
vals = data.values().iter().map(str.upper).sort()

assert vals.first() == "ALICE"
assert vals.len() == 4
# Modify the Vec in place with retain according to the predicate
vals.retain(lambda x: x.endswith("E"))
assert vals.len() == 3

# Create a set of unique names, preserving insertion order, with StableSet
unique_names = vals.pipe(StableSet)
assert unique_names == {"ALICE", "CHARLIE"}
assert unique_names.iter().next() == Some("ALICE")

Result and Option

Handle None and exceptions in an explicit way with dedicated types, instead of relying on implicit truthiness checks or try/except blocks.

Success and failure can respectively be represented by Ok[T] and Err[E] types, while optional values can be represented by Some[T] and Null.

This make each path explicit, less error-prone, and more readable, by replacing nested try/except blocks and if x is not None checks by a single pipeline like x.map().and_then().unwrap_or().

For users familiar with this pattern, almost all methods present in their Rust counterparts are available, as well as additional convenience methods for broad python ecosystem compatibility, like unwrap_or_none() (heresy, I know).

from pyochain import Option, NONE, Some, Seq, Vec, Set, Ok, Err, Result
from pyochain.abc import PyoIterable


def divide(a: int, b: int) -> Option[float]:
    return NONE if b == 0 else Some(a / b)


assert divide(10, 2) == Some(5.0)
# Provide a default value
assert divide(10, 0).unwrap_or(-1.0) == -1.0
# Convert between Collections -> Option -> Result
tup = (1, 2, 3)
seq = Seq(tup)
assert seq.then_some() == Some(Seq(tup))
assert seq.then_some().ok_or("No values").unwrap() == Seq(tup)


# Accept any Pyochain Iterable
def _process(data: PyoIterable[int]) -> str:
    return data.iter().map(str).join(", ")


# Process only if non-empty, convert Option to Result
assert seq.then(_process).ok_or("No values").unwrap() == "1, 2, 3"
assert (
    Vec().then(_process).ok_or("No values").expect_err("expected error") == "No values"
)
# Create empty Set, convert to Result, then back to Option
assert Set().then(_process).ok_or("No values").ok() is NONE

Type safe exhaustive handling with pattern matching

Type checkers will ensure that all cases are handled when matching on Option and Result types.

from pyochain import Result, Ok, Err


def try_parse_int(s: str) -> Result[int, ValueError]:
    try:
        return Ok(int(s))
    except ValueError as e:
        return Err(e)


def handle_result(res: Result[int, ValueError]) -> str:
    match res:
        case Ok(value):
            return f"Parsed value: {value}"
        case Err(_):
            return f"Error parsing int!"


assert try_parse_int("123").pipe(handle_result) == "Parsed value: 123"
assert try_parse_int("abc").pipe(handle_result) == "Error parsing int!"

ABC's

A class hierarchy mimicking the collections.abc module is provided, each only requiring the same dunders as it's standard library counterpart (e.g __iter__ for PyoIterable), while providing many rust-inspired additional methods, like PyoMutableSequence::retain, PyoMutableMapping::try_insert, and of course the numerous functionnalities from PyoIterator.

All concretes Iterator and Collection types implement them, so you can use them for type checking, implement your own subclasses, and seamlessly replace python ecosystem types, without trade-offs.

from pyochain import Some
from pyochain.abc import PyoSequence, PyoIterable
from dataclasses import dataclass
from collections.abc import Sequence


@dataclass(slots=True)
class MySequence(PyoSequence[int]):
    data: list[int]

    def __len__(self) -> int:
        return len(self.data)

    def __getitem__(self, index: int) -> int:
        return self.data[index]


x = MySequence([1, 2, 3])
# Call any method from PyoSequence, like first(), last(), get(), etc...
assert x.get(2) == Some(3)
# Convert to an Iterator and immediately benefit from all the Iterator methods
assert x.iter().map(lambda x: x**2).collect(tuple) == (1, 4, 9)
# Convert to an Option just like pyochain core API
assert x.then_some() == Some(x)
# Works with runtime instance and subclass type checking
assert isinstance(x, PyoSequence)
assert isinstance(x, PyoIterable)
assert isinstance(x, Sequence)
assert issubclass(MySequence, PyoSequence)
assert issubclass(MySequence, Sequence)

Notice on Stability ⚠️

pyochain is currently in early development (< 1.0), and the API may undergo significant changes multiple times before reaching a stable 1.0 release.

Contributing

We are actively looking for contributors to help us improve pyochain! If you are interested in contributing, please read our contributing guide for more information on how to get started.

Credits

Most of the custom computation algorithms have been inspired by implementations from itertools, cytoolz and more-itertools.

Pyo3 is used to compile the library in Rust, and provide a seamless integration with Python.

Using Polars made me realize that reading my code from top to bottom was a better way to write python, and also introduced me to Rust.

Star History

Star History Chart

Metadata

Release files for pyochain 0.28.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyochain 0.28.0
File Size Uploaded
pyochain-0.28.0.tar.gz 318.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for pyochain 0.28.0
File
pyochain-0.28.0-cp314-cp314-win_amd64.whl CPython 3.14 CPython 3.14 Windows x86-64 Details
pyochain-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64 Details
pyochain-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ ARM64 Details
pyochain-0.28.0-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details
pyochain-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl CPython 3.14 CPython 3.14 macOS 10.12+ x86-64 Details
pyochain-0.28.0-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
pyochain-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64 Details
pyochain-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ ARM64 Details
pyochain-0.28.0-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details
pyochain-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl CPython 3.13 CPython 3.13 macOS 10.12+ x86-64 Details

Total release size: 10.5 MB

Release files / pyochain-0.28.0.tar.gz

Download URL pyochain-0.28.0.tar.gz
Size 318.2 kB
Tags Source
SHA-256 checksum
How to use checksums
bf4e526d09f1a930ccb13e4e860b464efec2a4a9c00c71f31c9ef130e1c1efbd
BLAKE2b-256 checksum
How to use checksums
e065df1a4179e44a765b874969c8688b0371e7da4f9c62ba5dc855e1d2f84000
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp314-cp314-win_amd64.whl

Download URL pyochain-0.28.0-cp314-cp314-win_amd64.whl
Size 958.6 kB
Tags CPython 3.14 Windows x86-64
SHA-256 checksum
How to use checksums
ea67a699727688c5ca88db4a45745d8e60201bdd857bb030d06ae8bbfdd6cd42
BLAKE2b-256 checksum
How to use checksums
7735dcce7e7215d8da5994ee06bbdc79e56f6d7859c702bf75175b3236a7c073
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pyochain-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.0 MB
Tags CPython 3.14 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
eb7503bd88b0b7a02d9bb24d8ceb0a461a77d539d8168c5467046ec7234dfae1
BLAKE2b-256 checksum
How to use checksums
b7a193653b3fdb072b3585df1f468b68b639564d8f83fe5bde69377c258bfac3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL pyochain-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.0 MB
Tags CPython 3.14 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
95e43d08bc80e8a58e36ef0f1a83d1a11fc95e53ea95d8fda72005866a526f37
BLAKE2b-256 checksum
How to use checksums
6b330264351d0746de4be69d45e70e87464ab75104a0cd1f9499bdac7947b3fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp314-cp314-macosx_11_0_arm64.whl

Download URL pyochain-0.28.0-cp314-cp314-macosx_11_0_arm64.whl
Size 989.2 kB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4ac79c3cf51478b8193bf4ad6e753f9f4c60063ec30a38f0faa81f4fb261b167
BLAKE2b-256 checksum
How to use checksums
f572d0d8ad1cac5599bc7e51f4cc3059239c712b1e89123528f386c5cd97314b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl

Download URL pyochain-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl
Size 1.1 MB
Tags CPython 3.14 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
e16fadd775a812e802684ee8bf57f00af0371a2dbb3eb21f04ffe5d9efa043f7
BLAKE2b-256 checksum
How to use checksums
06cab70487804883f4a620e7d80f2f06fa04a0eaf39055947db14fb6ead4e443
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp313-cp313-win_amd64.whl

Download URL pyochain-0.28.0-cp313-cp313-win_amd64.whl
Size 959.0 kB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
0e76f9b337002562f95ebaa138835610f462da7d3485cc4bc3e3f363c8d9acc7
BLAKE2b-256 checksum
How to use checksums
f186a419042e1b1b1a61a486e7c01a2287985259c4476abed71416466a4d86be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pyochain-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.0 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
d6798799ce188140ad3fd555f4cf41624fa86445eb0b1b0739536cd8379d2f8a
BLAKE2b-256 checksum
How to use checksums
1260017d9b335c0e7b38959c05f96e2b70b6ed81252ada634dbae88d30f8be1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL pyochain-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.0 MB
Tags CPython 3.13 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
2a0816e3f0b85283d78a82a260aa4ce2e92a8dbd9f68260fb5cfcc67a4afa6c8
BLAKE2b-256 checksum
How to use checksums
211bae827fb042ff940b8c1e5acda29b36a589bb30984810c42a1fea3f023f4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp313-cp313-macosx_11_0_arm64.whl

Download URL pyochain-0.28.0-cp313-cp313-macosx_11_0_arm64.whl
Size 989.2 kB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1940a2e5406bb0fc0c1c9c78ac5cf4ddefcff9b35f104059c0c38a2c0fedaf9f
BLAKE2b-256 checksum
How to use checksums
c093ce572c70219d8e71cd17e72067c02daa3f4272f02ea2cc21353df0a9d5ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyochain-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl

Download URL pyochain-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl
Size 1.1 MB
Tags CPython 3.13 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
d3cf7b77edafaba6f2624eefb2cc55d593d5f37661d0fa789ddd7aaa34c6a312
BLAKE2b-256 checksum
How to use checksums
5f69333b8acef4756adf1a8c7c8b83af60328f9a12d508fb47f934158b8a489d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release history Release notifications | RSS feed

This release

0.28.0 This release

11 release files

0.9.3

11 release files

0.9.2

11 release files

0.9.1

6 release files

0.9.0

6 release files

0.8.3

6 release files

0.8.0

1 release file

0.7.0

2 release files

0.6.6

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.5.51

2 release files

0.5.50

2 release files

0.5.46

2 release files

0.5.45

2 release files

0.5.44

2 release files

0.5.43

2 release files

0.5.42

2 release files

0.5.41

2 release files

0.5.32

2 release files

0.5.6

2 release files

0.5.5

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page