Dataframe With Pause

Dataframe with pause example.

Example workflow demonstrating DataFrame serialization with pause/resume.

Run it

python examples/dataframe_with_pause.py
"""
Example workflow demonstrating DataFrame serialization with pause/resume.

This example shows how to use LocalFileStorage to properly serialize
pandas DataFrames across workflow pause/resume cycles, ensuring data
integrity is maintained.

Usage:
    flux workflow run dataframe_with_pause '{"file_path": "path/to/data.csv"}'
    flux workflow resume dataframe_with_pause <execution_id> '{}'
"""

from __future__ import annotations

from typing import Any

import pandas as pd

from flux import ExecutionContext, task, workflow
from flux.output_storage import LocalFileStorage
from flux.tasks import pause

file_storage = LocalFileStorage()


@task.with_options(output_storage=file_storage)
async def load_csv(file_path: str) -> pd.DataFrame:
    """Load CSV file into a DataFrame."""
    return pd.read_csv(file_path)


@task.with_options(output_storage=file_storage)
async def process_dataframe(df: pd.DataFrame) -> dict[str, Any]:
    """Process the DataFrame and return summary statistics."""
    return {
        "row_count": len(df),
        "column_count": len(df.columns),
        "columns": list(df.columns),
        "first_row": df.head(1).to_dict(orient="records")[0] if len(df) > 0 else {},
    }


@workflow
async def dataframe_with_pause(ctx: ExecutionContext[dict[str, str]]):
    """
    Workflow that validates DataFrame integrity across pause/resume.

    This workflow demonstrates that DataFrames are properly preserved
    when using LocalFileStorage with pause/resume functionality.
    """
    file_path = ctx.input.get("file_path")

    # Load CSV
    df = await load_csv(file_path)

    # Validate required columns exist
    required_columns = {"product", "quantity", "revenue"}
    if not required_columns.issubset(df.columns):
        missing = required_columns - set(df.columns)
        raise ValueError(f"Missing required columns: {missing}")

    # Capture values before pause to validate integrity after resume
    before_pause_values = {
        "shape": df.shape,
        "first_row_product": df.iloc[0]["product"],
        "first_row_revenue": float(df.iloc[0]["revenue"]),
        "first_row_quantity": int(df.iloc[0]["quantity"]),
        "total_rows": len(df),
        "column_count": len(df.columns),
        "sum_revenue": float(df["revenue"].sum()),
    }

    # Pause here - DataFrame needs to be preserved
    resume_input = await pause("after_load")

    # Validate DataFrame was correctly restored
    after_pause_values = {
        "shape": df.shape,
        "first_row_product": df.iloc[0]["product"],
        "first_row_revenue": float(df.iloc[0]["revenue"]),
        "first_row_quantity": int(df.iloc[0]["quantity"]),
        "total_rows": len(df),
        "column_count": len(df.columns),
        "sum_revenue": float(df["revenue"].sum()),
    }

    # Verify all values match
    integrity_check = all(
        before_pause_values[k] == after_pause_values[k] for k in before_pause_values.keys()
    )

    # Process it after resume
    result = await process_dataframe(df)

    return {
        "status": "success" if integrity_check else "data_integrity_failed",
        "result": result,
        "before_pause": before_pause_values,
        "after_pause": after_pause_values,
        "integrity_check": integrity_check,
        "execution_id": ctx.execution_id,
        "resumed_with": resume_input,
    }

Last verified against Flux 0.56.0.