Skip to main content

Railway-oriented computation builders for typed Python workflows

Project description

Pythonic Computation Expression Builders

comp-builders

comp-builders banner

comp-builders brings railway-oriented programming patterns to typed Python through small, dependency-free computation builders. It lets you write readable workflows that sequence Result, Option, AsyncResult, and Validation values without deeply nested if statements or exception-heavy control flow.


Motivation and Purpose

Many production Python workflows have the same shape: parse input, validate it, call a service, transform a response, and stop early when something goes wrong. Traditional Python handles that with exceptions, sentinel values, or repeated conditional checks. Those techniques work, but they can hide failure paths and make the happy path harder to read.

Railway-oriented programming makes success and failure explicit. A workflow moves along a success track until a step returns a failure, absence, or validation error. comp-builders keeps that idea lightweight and Pythonic by using generator-based blocks rather than introducing a large functional programming framework.


Key features

  • Result, Ok, and Err for computations that can succeed or fail.
  • Option, Some, and Nothing for optional values without None checks scattered through a workflow.
  • AsyncResult for awaitable operations that return explicit success or failure values.
  • Validation, Valid, and Invalid for accumulating independent validation errors.
  • Decorator-based builders: @result.block, @option.block, @async_result.block, and @validation.block.
  • Typed public API with py.typed, runtime tests, and mypy coverage.
  • No runtime dependencies.

Railway-oriented programming in one minute

In functional languages, railway-oriented programming is often built from Result/Either types and bind operators:

let createUser raw =
  raw
  |> parseJson
  |> Result.bind readUserId
  |> Result.bind saveUser

A traditional Python version often repeats branching logic after every step:

def create_user(raw: str):
  parsed = parse_json(raw)
  if not parsed.ok:
    return parsed

  user_id = read_user_id(parsed.value)
  if not user_id.ok:
    return user_id

  return save_user(user_id.value)

With comp-builders, each yield unwraps an Ok value or short-circuits on Err:

from comp_builders import Err, Ok, Result, result


def parse_user_id(raw: str) -> Result[str, str]:
  value = raw.strip()
  return Ok(value) if value else Err("missing user id")


@result.block
def create_user_id(raw: str):
  user_id = yield parse_user_id(raw)
  return user_id.upper()


assert create_user_id(" ada ") == Ok("ADA")
assert create_user_id(" ") == Err("missing user id")

For a longer explanation with functional-language comparisons, traditional Python examples, and package-based Python examples, see Railway-Oriented Programming for Python Users.


How it works

ROP mechanism

The core abstraction is Builder.block. Concrete builders define how yielded values are unwrapped and how final values are wrapped. For example, ResultBuilder unwraps Ok(value), stops on Err(error), and wraps a completed return value as Ok(value).


Install

uv add comp-builders

For local development from this repository:

uv sync --all-groups
uv run pytest
uv run mypy .
uv run ruff check .

Minimal examples

Option

from comp_builders import Nothing, Some, option


@option.block
def first_initial(user: dict[str, object]):
  name = yield user.get("name", Nothing)
  return name[0]


assert first_initial({"name": Some("Ada")}) == Some("A")
assert first_initial({}) is Nothing

Validation

from comp_builders import Invalid, Valid, validation


@validation.block
def validate_user(data: dict[str, str]):
  name = yield Valid(data["name"]) if data.get("name") else Invalid("missing name")
  yield Valid(data["email"]) if "@" in data.get("email", "") else Invalid("invalid email")
  return name


assert validate_user({"name": "", "email": "bad"}) == Invalid(
  ("missing name", "invalid email")
)

Documentation


Compatibility

comp-builders supports Python 3.11 and newer. It is distributed as a typed package and includes a py.typed marker.


License

Apache License 2.0. See LICENSE.

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

comp_builders-1.0.0.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

comp_builders-1.0.0-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

Details for the file comp_builders-1.0.0.tar.gz.

File metadata

  • Download URL: comp_builders-1.0.0.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for comp_builders-1.0.0.tar.gz
Algorithm Hash digest
SHA256 cdeb53e582c1ec36496ef404da4e285cbe1e14be9311c7ceed695fa678d56ce7
MD5 5620b13f07466173e54bb2ea9ebf5755
BLAKE2b-256 f59d01553d85bacd086146574ddfde6acd6a99669f17c9f94bc2afa6ae064be3

See more details on using hashes here.

File details

Details for the file comp_builders-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: comp_builders-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 13.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for comp_builders-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 95b0854d6893ada24a258d6b23a973550d439898784a5307703fe67cf560e6e0
MD5 f808b766594f3b6d1b98cc900f415394
BLAKE2b-256 71456f539b5661311b0d374780c71888433466a77068e8dae3357217c7daa920

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page