Skip to main content

masoora

CI PyPI Python versions License: MIT Typed

A helper library for writing data pipelines in Python.

masoora handles the wiring — working out step order, passing data between steps, running independent steps at the same time — so your code stays focused on the transformations.

It works with your orchestrator rather than replacing it. Build a pipeline, package it as a versioned wheel, and call it from an Airflow task: Airflow passes its parameters in as the context, and the pipeline runs as one task. Changing the pipeline is a version bump, not an orchestrator deployment.

  • Readable: the builder chain is the pipeline — where data comes from, what happens to it, and where it goes, visible without running anything
  • Fluent chaining: .with_read_step() / .with_transform_step() / .with_write_step()
  • Pydantic context: typed configuration passed to every step, validated at the boundary
  • Data catalog: in-memory key → dataset store (polars/pandas/spark/dicts — anything)
  • DAG resolution: declare steps in any order; cycles and missing inputs fail at build()
  • Data validation: attach a pandera schema, or any callable, to a catalog key
  • Diagrams: pipeline.to_mermaid() renders the DAG, no dependencies
  • Parallel: dependency-driven scheduling, no level barriers
  • Testable: mock reads/writes and assert on the catalog with a pytest fixture

Installation

pip install masoora

Or with uv:

uv add masoora

Requires Python 3.10+. The only runtime dependency is Pydantic.

The pytest helpers (make_pipeline_fixture) need pytest, available as an extra:

pip install "masoora[pytest]"

Usage

from masoora import PipelineBuilder, PipelineContext


class MyContext(PipelineContext):
    source_url: str
    min_score: float = 0.5


def read_events(ctx: MyContext): ...
def score(ctx: MyContext, events): ...
def filter_top(ctx: MyContext, scored): ...
def write_db(ctx: MyContext, top): ...


pipeline = (
    PipelineBuilder[MyContext]()
    .with_read_step(read_events, output="events")
    .with_transform_step(score, inputs=["events"], output="scored")
    .with_transform_step(filter_top, inputs=["scored"], output="top")
    .with_write_step(write_db, inputs=["top"])
    .build()
)

catalog = pipeline.run(MyContext(source_url="https://..."))

pipeline.to_mermaid() renders the same pipeline as a diagram:

flowchart TD
    n0["read_events"]:::read
    n1["score"]:::transform
    n2["filter_top"]:::transform
    n3["write_db"]:::write
    n0 -->|"events"| n1
    n1 -->|"scored"| n2
    n2 -->|"top"| n3
    classDef read fill:#dbeafe,stroke:#2563eb,color:#0b2a5b;
    classDef transform fill:#e5e7eb,stroke:#4b5563,color:#111827;
    classDef write fill:#dcfce7,stroke:#16a34a,color:#052e16;
    classDef data fill:#fef3c7,stroke:#d97706,color:#451a03;

Step signatures:

Step kind Signature Effect
read fn(ctx) -> dataset catalog[output] = result
transform fn(ctx, *inputs) -> dataset catalog[output] = result
write fn(ctx, *inputs) -> None terminal

Steps may be declared in any order — build() topo-sorts them. Run only what's needed for one output with pipeline.run(ctx, target="top"). Pre-populated catalog keys are declared with .with_seed(key).

Parallel execution

pipeline.run(ctx, parallel=True)  # thread pool, os.cpu_count() workers
pipeline.run(ctx, parallel=4)  # explicit worker count
pipeline.run(ctx, executor=pool)  # your Executor (not shut down by masoora)

Steps run concurrently in a ThreadPoolExecutor with dependency-driven scheduling: each step starts the instant its own dependencies finish — there is no level barrier, so unrelated slow steps never delay a ready branch. Fail-fast: the first step error cancels queued work and raises StepExecutionError immediately; already-running siblings finish in the background.

Contract: steps must only read their declared input keys, write their own output key, and treat the context as read-only. Under this contract parallel results are identical to sequential.

Testing

from masoora import TestRunResult, make_pipeline_fixture

run_pipeline = make_pipeline_fixture(
    pipeline,
    MyContext(source_url="test"),
    reads={"events": fake_events},  # read step is replaced, real source untouched
)


def test_top_events(run_pipeline: TestRunResult[MyContext]) -> None:
    assert run_pipeline.catalog["top"] == expected
    assert run_pipeline.written["top"] == expected  # write step captured, not executed

Or without pytest: pipeline.to_testable(reads={...}).run(ctx)TestRunResult.

Development

uv sync
uv run pytest
uv run ruff check .
uv run mypy src tests

Issues and pull requests are welcome. The API is still young — if something feels awkward to use, that is worth an issue.

Links

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

masoora-0.3.0.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

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

masoora-0.3.0-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file masoora-0.3.0.tar.gz.

File metadata

  • Download URL: masoora-0.3.0.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for masoora-0.3.0.tar.gz
Algorithm Hash digest
SHA256 8bd3eab3a4802e742e0ccbce14cf2d0457e450797b584530d7b585549460ad04
MD5 fc4cea71d18556d5fd0164076d416f13
BLAKE2b-256 2a8d3c7dfa75c641663490af1d6a4032d133f4b0198e04d70c7f42ebbd70291a

See more details on using hashes here.

Provenance

The following attestation bundles were made for masoora-0.3.0.tar.gz:

Publisher: publish.yml on ahmedtilal/masoora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file masoora-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: masoora-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for masoora-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8d1fbfbcae5f534609899c50b3a13caf73fb0e29830fa79ebfb3ec18a3af60e3
MD5 e09950c90ce055f784979aaa2928b78f
BLAKE2b-256 29f30e5b6feb850b413865cf893109306e2727d55a102d403c5a28de21786efc

See more details on using hashes here.

Provenance

The following attestation bundles were made for masoora-0.3.0-py3-none-any.whl:

Publisher: publish.yml on ahmedtilal/masoora

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 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