Skip to main content

A lightweight Python library for building interruptible, generator-driven ETL pipelines as directed graphs.

Project description

yieldgraph

Build interruptible, generator-driven ETL pipelines as directed graphs — in pure Python.

CI Python 3.14+ License: MIT Docs


What is yieldgraph?

yieldgraph lets you compose data-processing pipelines by connecting plain Python callables into a directed graph. Each callable becomes a Node. Nodes are linked by Edges — lightweight queues that carry data tuples downstream. Call graph.run() and the pipeline does the rest.

There is no framework to learn. Your business logic stays in ordinary Python functions. yieldgraph just wires them together and gets out of the way.


Features

  • Pure Python, zero runtime dependencies — stdlib only; loguru is optional.
  • Generator-nativeyield zero, one, or many results per input; everything stays lazy.
  • Cooperative cancellation — set graph.cancelled = True or press Ctrl+C; the pipeline stops cleanly at the next yield.
  • Fan-out branching — attach multiple downstream chains to any node with a single add_chain call.
  • Threaded mode — flip YIELDGRAPH_THREADED=1 to run all nodes concurrently; edges become thread-safe blocking queues automatically.
  • Built-in observability — every node exposes n_consumed, n_produced, errors, and a progress fraction you can poll at any time.

Installation

pip install yieldgraph

From source:

git clone https://github.com/j4ggr/yieldgraph.git
cd yieldgraph
pip install -e .

Quick start

from yieldgraph import Graph

def source(graph):
    """Emit raw records — receives the Graph instance as first argument."""
    for row in [
        {"name": "Alice", "score": 95},
        {"name": "Bob",   "score": 72},
        {"name": "Carol", "score": 88},
    ]:
        yield row

def grade(record):
    """Add a pass/fail label to each record."""
    record["grade"] = "pass" if record["score"] >= 85 else "fail"
    yield record

def format_output(record):
    """Format as a human-readable string."""
    yield f"{record['name']}: {record['grade']} ({record['score']})"

g = Graph()
g.add_chain(source, grade, format_output)
g.run()

for row in g.output:
    print(row[0])
# Alice: pass (95)
# Bob: fail (72)
# Carol: pass (88)

Fan-out — multiple downstream chains

def source(graph):
    for x in range(1, 6):
        yield x

def store_db(x):
    yield f"db:{x}"

def send_queue(x):
    yield f"mq:{x}"

g = Graph()
g.add_chain(source, store_db)    # chain 1
g.add_chain(source, send_queue)  # chain 2 — same source, parallel branch
g.run()

print(g.output)
# [('db:1',), ('db:2',), ..., ('mq:1',), ('mq:2',), ...]

Cooperative cancellation

def source(graph):
    for i in range(1_000_000):
        if i >= 5:
            graph.cancelled = True   # stop cleanly after this item
            return
        yield i

g = Graph()
g.add_chain(source, process)
g.run()

print(len(g.output))   # 5
print(g.cancelled)     # True

Threaded execution

YIELDGRAPH_THREADED=1 python my_pipeline.py

Or in Python before the run:

import os
os.environ["YIELDGRAPH_THREADED"] = "1"

g = Graph()
g.add_chain(fetch_from_api, transform, write_to_db)
g.run()

Error handling

Exceptions raised inside a node are caught per-item and stored — they never abort the pipeline. Inspect them after the run:

g.run()

for name, node in g.nodes.items():
    if node.n_errors:
        print(f"{name}: {node.n_errors} error(s)")
        for err in node.errors:
            print(f"  {type(err).__name__}: {err}")

if g.succeeded:
    print(f"Done — {len(g.output)} rows produced")

Status & progress

Expression Type Description
g.output list[tuple] All tuples emitted by terminal nodes
g.succeeded bool Finished without errors
g.has_output bool Finished and produced at least one row
g.error str Graph-level error description (empty on success)
g.step str Human-readable label of the currently executing node
g.progress int 0–100 % progress of the current node
g.cancelled bool Whether the run was cancelled
g.finished bool Whether run() has returned

Documentation

Full guides and API reference at https://j4ggr.github.io/yieldgraph/


Development

# Install dev dependencies
pdm install -G test -G doc

# Run tests
pdm run pytest

# Build docs locally
pdm run mkdocs serve

License

MIT

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

yieldgraph-0.3.1.tar.gz (33.8 kB view details)

Uploaded Source

Built Distribution

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

yieldgraph-0.3.1-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

File details

Details for the file yieldgraph-0.3.1.tar.gz.

File metadata

  • Download URL: yieldgraph-0.3.1.tar.gz
  • Upload date:
  • Size: 33.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.26.2 CPython/3.12.3 Windows/11

File hashes

Hashes for yieldgraph-0.3.1.tar.gz
Algorithm Hash digest
SHA256 6cb98ffc473e1bcf4d95cb53e11b6d2b3cea27c5a36cee3ef2cbee6791314207
MD5 d0f8dbd5587a00f9694a2693fd0ca7e4
BLAKE2b-256 8cb31b28d90c907c65576456fda4a717026b855866cda98e5f7932f13bade36f

See more details on using hashes here.

File details

Details for the file yieldgraph-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: yieldgraph-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 25.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.26.2 CPython/3.12.3 Windows/11

File hashes

Hashes for yieldgraph-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ba601c141b0ec43b37d3f1b5bb2a9a40892ea3513d2e5e01e6992250b5f37f6c
MD5 b73c007101356d23a05839a64312b7d0
BLAKE2b-256 34793140dbdc593c203c2cda370a8c626587430ff4f3ecc2483aada03c7b2038

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