Skip to main content

Logo

Laufband: Embarrassingly parallel, embarrassingly simple!

codecov

Laufband enables parallel iteration over a dataset from multiple processes, utilizing file-based locking and communication to ensure each item is processed exactly once.

Installation

Install Laufband using pip:

pip install laufband

Usage

Using Laufband is similar to the familiar tqdm progress bar for sequential iteration.

from laufband import Laufband

data = list(range(100))
for item in Laufband(data):
    # Process each item in the dataset
    pass

The true power of Laufband emerges when you run your script in parallel. Multiple processes will coordinate using file-based locking to ensure that each item in the dataset is processed by only one process.

Here's a typical example demonstrating parallel processing with Laufband and file-based locking for shared resource access:

import json
import time
from pathlib import Path
from laufband import Laufband

output_file = Path("data.json")
output_file.write_text(json.dumps({"processed_data": []}))
data = list(range(100))

worker = Laufband(data, tqdm_kwargs={"desc": "using Laufband"})

for item in worker:
    # Simulate some computationally intensive task
    time.sleep(0.1)
    with worker.lock:
        # Access and modify a shared resource (e.g., a file) safely using the lock
        file_content = json.loads(output_file.read_text())
        file_content["processed_data"].append(item)
        output_file.write_text(json.dumps(file_content))

To execute this script (main.py) in parallel, you can use a command like the following in your terminal (this example launches 10 background processes):

for i in {1..10} ; do python main.py & done

Failure Policy

In Laufband, a job will be automatically marked as failed if the iteration is interrupted by:

  • an unhandled Exception
  • or an explicit break.
from laufband import Laufband

data = list(range(100))

# Example 1: break
for item in Laufband(data):
    if item == 50:
        break  # Job 50 will be marked as failed

# Example 2: Exception
for item in Laufband(data):
    if item == 70:
        raise ValueError("Something went wrong")  # Job 70 will be marked as failed

If you want to exit early but still mark the job as successfully completed, you should use Laufband.close() instead of break:

from laufband import Laufband

data = list(range(100))

worker = Laufband(data)

for item in worker:
    if item == 50:
        worker.close()  # Job 50 will be marked as completed, and iteration will stop cleanly

Context Manager Protocol

Laufband supports context manager usage for better worker lifecycle management. This is recommended, if you want to iterate over the Laufband or Graphband instance multiple times.

from laufband import Laufband

data = list(range(100))

# Using context manager for multiple iterations (recommended)
with Laufband(data) as worker:
    while True:
        for item in worker:
            # do something
            pass
# Worker automatically goes offline when exiting context

Context Manager Benefits:

  • Proper cleanup: Workers are automatically set to OFFLINE status when exiting the context

Worker Status Lifecycle:

  • Without context manager: Worker goes from IDLE → BUSY → OFFLINE (after completing all tasks)
  • With context manager: Worker goes from IDLE → BUSY → IDLE (during processing) → OFFLINE (on context exit)

Examples

ASE Calculator

For atomistic data, the ASE package is widely used to calculate energies and forces of atomic configurations using either ab initio methods or machine-learned interatomic potentials (MLIPs).

You can use Laufband to parallelize these calculations easily without duplication or manual bookkeeping and automatic checkpointing.

The following example uses a MACE foundation model to compute energies and forces on the ASE S22 dataset.

import ase.io
from ase.collections import s22
from laufband import Laufband
from mace.calculators import mace_mp

# Initialize calculator
calc = mace_mp(model="medium", dispersion=False, default_dtype="float32")

worker = Laufband(list(s22))

for atoms in worker:
    atoms.calc = calc
    energy = atoms.get_potential_energy()
    worker.iterator.set_description(f"{energy = }")
    with worker.lock:
        ase.io.write("frames.xyz", atoms, append=True)

You can use the laufband watch to follow the progress across all active workers.

Laufband CLI Laufband CLI

Graphband

Laufband supports dependency-aware tasks through laufband.Graphband. To use this, provide an iterator that yields tasks in a valid execution order.

import networkx as nx
from laufband import Task

def graph_tasks():
    digraph = nx.DiGraph()
    edges = [
        ("a", "b"),
        ("a", "c"),
        ("b", "d"),
        ("b", "e"),
        ("c", "f"),
        ("c", "g"),
    ]
    digraph.add_edges_from(edges)
    for node in nx.topological_sort(digraph):
        yield Task(
            id=node,  # unique string representation of the task
            data=node, # optional data associated with the task
            dependencies=set(digraph.predecessors(node)), # dependencies of the task
        )

Given this generator, you can iterate the graph in parallel using laufband.Graphband.

from laufband import Graphband

worker = Graphband(graph_tasks())

for task in worker:
    print(task.id, task.data)

Labels, Requirements, and Multiple Workers per Task

You can assign requirements to your tasks and labels to workers to control their execution.

from laufband import Task, Graphband

def iterator():
    yield Task(id="task1")
    yield Task(id="task2", requirements={"gpu"})

w1 = Graphband(iterator(), identifier="w1")
w2 = Graphband(iterator(), identifier="w2", labels={"gpu"})

print([x.id for x in w1])
# ["task1"]
print([x.id for x in w2])
# ["task2"]

Sometimes a task supports internal parallel execution (e.g., nested use of laufband). In such a case, you can assign multiple workers to one task.

from laufband import Task, Graphband

def iterator():
    yield Task(id="task1", max_parallel_workers=2)
    # At most 2 workers will be assigned to this job until both successfully finish.

worker = Graphband(iterator())

for item in worker:
    # Code that can be executed multiple times, e.g., via laufband itself.
    ...

Metadata

Release files for laufband 0.2.1

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

Source distribution (sdist)

Source distribution for laufband 0.2.1
File Size Uploaded Provenance
laufband-0.2.1.tar.gz 198.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for laufband 0.2.1
File Interpreter ABI Platform
laufband-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 217.9 kB

Release files / laufband-0.2.1.tar.gz

Download URL laufband-0.2.1.tar.gz
Size 198.1 kB
Tags Source
SHA-256 checksum
How to use checksums
550083fe4bce1a95db99f4b61d9ec523121c9e37d8cbf352d02325fb2b4da06a
BLAKE2b-256 checksum
How to use checksums
9ca854a04b70936d162395c556c859047810da1a2829e5d9e077228f0132b8c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release files / laufband-0.2.1-py3-none-any.whl

Download URL laufband-0.2.1-py3-none-any.whl
Size 19.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d9adcae469029bf06a0ac3a9741daceb7f754b10d0661cd8b0184eb45589f019
BLAKE2b-256 checksum
How to use checksums
caccae496d34403f0e27a845d8ebb63dc32c8ce0418c4df34a97ddef188c6fbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Security for laufband 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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