fast-dag


Namefast-dag JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryAdd your description here
upload_time2025-07-16 21:08:37
maintainerFelix Geilert
docs_urlNone
authorFelix Geilert
requires_python>=3.10
licenseMIT
keywords dag workflow workflow-engine
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # fast-dag

<p align="center">
  <a href="https://pypi.org/project/fast-dag/"><img src="https://img.shields.io/pypi/v/fast-dag.svg" alt="PyPI version"></a>
  <a href="https://pypi.org/project/fast-dag/"><img src="https://img.shields.io/pypi/pyversions/fast-dag.svg" alt="Python versions"></a>
  <a href="https://github.com/felixnext/fast-dag/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
  <a href="https://github.com/felixnext/fast-dag/actions"><img src="https://github.com/felixnext/fast-dag/workflows/CI/badge.svg" alt="CI"></a>
</p>

**fast-dag** is a lightweight, high-performance Python library for building and executing Directed Acyclic Graph (DAG) and Finite State Machine (FSM) workflows. It's designed with simplicity and speed in mind, making it perfect for data pipelines, task orchestration, and complex workflow automation.

## ✨ Key Features

- 🚀 **Fast & Lightweight** - Minimal dependencies, optimized for performance
- 🔄 **DAG & FSM Support** - Build both directed acyclic graphs and state machines
- 🎯 **Type-Safe** - Full type hints and mypy support
- 🔌 **Extensible** - Easy to add custom nodes and behaviors
- 📊 **Visualization** - Built-in Mermaid and Graphviz support
- ⚡ **Multiple Execution Modes** - Sequential, parallel, and async execution
- 🛡️ **Robust Error Handling** - Comprehensive error strategies and retry logic
- 🔍 **Runtime Validation** - Automatic cycle detection and dependency validation

## 📦 Installation

### Basic Installation

```bash
pip install fast-dag
```

### With Visualization Support

```bash
pip install fast-dag[viz]
```

### With All Features

```bash
pip install fast-dag[all]
```

### Development Installation

```bash
git clone https://github.com/felixnext/fast-dag.git
cd fast-dag
pip install -e ".[dev]"
```

## 🚀 Quick Start

### Simple DAG Example

```python
from fast_dag import DAG

# Create a DAG
dag = DAG("data_pipeline")

# Define nodes using decorators
@dag.node
def fetch_data() -> dict:
    return {"data": [1, 2, 3, 4, 5]}

@dag.node
def process_data(data: dict) -> list:
    return [x * 2 for x in data["data"]]

@dag.node
def save_results(processed: list) -> str:
    return f"Saved {len(processed)} items"

# Connect nodes
dag.connect("fetch_data", "process_data", input="data")
dag.connect("process_data", "save_results", input="processed")

# Execute the DAG
result = dag.run()
print(result)  # "Saved 5 items"
```

### FSM Example

```python
from fast_dag import FSM
from fast_dag.core.types import FSMReturn

# Create a finite state machine
fsm = FSM("traffic_light")

@fsm.state(initial=True)
def red() -> FSMReturn:
    print("Red light - STOP")
    return FSMReturn(next_state="green")

@fsm.state
def green() -> FSMReturn:
    print("Green light - GO")
    return FSMReturn(next_state="yellow")

@fsm.state
def yellow() -> FSMReturn:
    print("Yellow light - SLOW DOWN")
    return FSMReturn(next_state="red")

# Run for 5 cycles
fsm.max_cycles = 5
fsm.run()
```

## 🎯 Core Concepts

### Nodes

Nodes are the basic units of work in fast-dag:

```python
@dag.node
def my_task(x: int) -> int:
    return x * 2

# Or create nodes manually
from fast_dag.core.node import Node

node = Node(
    func=my_function,
    name="my_node",
    description="Process data"
)
```

### Connections

Connect nodes to define data flow:

```python
# Simple connection
dag.connect("source", "target")

# Specify input/output names
dag.connect("source", "target", output="result", input="data")

# Use operator syntax
dag.nodes["a"] >> dag.nodes["b"] >> dag.nodes["c"]
```

### Conditional Flows

Build dynamic workflows with conditions:

```python
@dag.condition
def check_threshold(value: int) -> bool:
    return value > 100

@dag.node
def process_high(value: int) -> str:
    return f"High value: {value}"

@dag.node
def process_low(value: int) -> str:
    return f"Low value: {value}"

# Connect conditional branches
dag.nodes["check_threshold"].on_true >> dag.nodes["process_high"]
dag.nodes["check_threshold"].on_false >> dag.nodes["process_low"]
```

### Advanced Node Types

```python
# Multi-input convergence
@dag.any()  # Waits for ANY input to be ready
def merge_first(data: dict) -> str:
    return f"Received: {data}"

@dag.all()  # Waits for ALL inputs to be ready
def merge_all(data: dict) -> str:
    return f"All data: {data}"

# Multi-way branching
@dag.select
def router(request: dict) -> SelectReturn:
    category = request.get("category", "default")
    return SelectReturn(branch=category, value=request)
```

## 🔧 Advanced Features

### Execution Modes

```python
# Sequential execution (default)
result = dag.run()

# Parallel execution
result = dag.run(mode="parallel")

# Async execution
result = await dag.run_async()

# Custom runner configuration
from fast_dag.runner import DAGRunner

runner = DAGRunner(dag)
runner.configure(
    max_workers=4,
    timeout=300,
    error_strategy="continue"
)
result = runner.run()
```

### Error Handling

```python
# Retry with exponential backoff
@dag.node(retry=3, retry_delay=1.0)
def flaky_operation() -> dict:
    # Will retry up to 3 times with exponential backoff
    return fetch_external_data()

# Error strategies
result = dag.run(error_strategy="continue")  # Continue on error
result = dag.run(error_strategy="stop")      # Stop on first error (default)
```

### Node Lifecycle Hooks

```python
def before_node(node, inputs):
    print(f"Starting {node.name}")

def after_node(node, inputs, result):
    print(f"Finished {node.name}: {result}")
    return result  # Can modify result

def on_error(node, inputs, error):
    print(f"Error in {node.name}: {error}")

dag.set_node_hooks(
    "my_node",
    pre_execute=before_node,
    post_execute=after_node,
    on_error=on_error
)
```

### Visualization

```python
# Generate Mermaid diagram
mermaid_code = dag.visualize(backend="mermaid")

# Generate Graphviz DOT
dot_code = dag.visualize(backend="graphviz")

# Visualize with execution results
dag.run()
from fast_dag.visualization import VisualizationOptions

options = VisualizationOptions(
    show_results=True,
    direction="LR",
    success_color="#90EE90",
    error_color="#FFB6C1"
)
dag.visualize(options=options, filename="pipeline", format="png")
```

### Context and Metrics

```python
# Access context during execution
@dag.node
def process_with_context(data: dict, context: Context) -> dict:
    # Access previous results
    previous = context.get("previous_node")
    
    # Store metadata
    context.metadata["process_time"] = time.time()
    
    return {"combined": [data, previous]}

# After execution, access metrics
dag.run()
print(dag.context.metrics["execution_order"])
print(dag.context.metrics["node_times"])
print(dag.context.metadata)
```

## 📋 More Examples

### Data Processing Pipeline

```python
dag = DAG("etl_pipeline")

@dag.node
def extract_data() -> pd.DataFrame:
    return pd.read_csv("data.csv")

@dag.node
def transform_data(df: pd.DataFrame) -> pd.DataFrame:
    return df.dropna().reset_index(drop=True)

@dag.node
def validate_data(df: pd.DataFrame) -> pd.DataFrame:
    assert len(df) > 0, "No data after transformation"
    return df

@dag.node
def load_data(df: pd.DataFrame) -> str:
    df.to_parquet("output.parquet")
    return f"Loaded {len(df)} rows"

# Connect the pipeline
(
    dag.nodes["extract_data"] 
    >> dag.nodes["transform_data"] 
    >> dag.nodes["validate_data"] 
    >> dag.nodes["load_data"]
)

result = dag.run()
```

### State Machine with Conditions

```python
fsm = FSM("order_processor")

@fsm.state(initial=True)
def pending(order_id: str) -> FSMReturn:
    # Process payment
    if payment_successful():
        return FSMReturn(next_state="confirmed", value={"order_id": order_id})
    else:
        return FSMReturn(next_state="failed", value={"reason": "payment_failed"})

@fsm.state
def confirmed(data: dict) -> FSMReturn:
    # Ship order
    tracking = ship_order(data["order_id"])
    return FSMReturn(next_state="shipped", value={"tracking": tracking})

@fsm.state(terminal=True)
def shipped(data: dict) -> FSMReturn:
    notify_customer(data["tracking"])
    return FSMReturn(stop=True, value="Order delivered")

@fsm.state(terminal=True)
def failed(data: dict) -> FSMReturn:
    log_failure(data["reason"])
    return FSMReturn(stop=True, value="Order failed")

result = fsm.run(order_id="12345")
```

## 🛠️ Development

### Running Tests

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=fast-dag

# Run specific test file
uv run pytest tests/unit/test_dag.py
```

### Code Quality

```bash
# Format code
uv run ruff format .

# Lint code
uv run ruff check . --fix

# Type checking
uv run mypy fast-dag
```

### Pre-commit Hooks

```bash
# Install pre-commit hooks
uv run pre-commit install

# Run manually
uv run pre-commit run --all-files
```

### Building and Publishing

```bash
# Build the package
uv run python -m build

# Check the distribution
uv run twine check dist/*

# Upload to TestPyPI (for testing)
uv run twine upload --repository testpypi dist/*

# Upload to PyPI
uv run twine upload dist/*
```

## 📚 Documentation

For more detailed documentation, examples, and API reference, visit our [documentation site](https://github.com/felixnext/fast-dag/wiki).

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

- Built with [UV](https://github.com/astral-sh/uv) for fast, reliable Python package management
- Inspired by [Airflow](https://airflow.apache.org/), [Prefect](https://www.prefect.io/), and [Dagster](https://dagster.io/)
- Special thanks to all contributors and users

## 📧 Contact

- **Author**: Felix Geilert
- **GitHub**: [@felixnext](https://github.com/felixnext)
- **PyPI**: [fast-dag](https://pypi.org/project/fast-dag/)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "fast-dag",
    "maintainer": "Felix Geilert",
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "dag, workflow, workflow-engine",
    "author": "Felix Geilert",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/e0/1e/cb1d488df8b3066158d9a9d6274623da538e8fbce44e8320cf74ddfe3fe6/fast_dag-0.1.0.tar.gz",
    "platform": null,
    "description": "# fast-dag\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/fast-dag/\"><img src=\"https://img.shields.io/pypi/v/fast-dag.svg\" alt=\"PyPI version\"></a>\n  <a href=\"https://pypi.org/project/fast-dag/\"><img src=\"https://img.shields.io/pypi/pyversions/fast-dag.svg\" alt=\"Python versions\"></a>\n  <a href=\"https://github.com/felixnext/fast-dag/blob/main/LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License\"></a>\n  <a href=\"https://github.com/felixnext/fast-dag/actions\"><img src=\"https://github.com/felixnext/fast-dag/workflows/CI/badge.svg\" alt=\"CI\"></a>\n</p>\n\n**fast-dag** is a lightweight, high-performance Python library for building and executing Directed Acyclic Graph (DAG) and Finite State Machine (FSM) workflows. It's designed with simplicity and speed in mind, making it perfect for data pipelines, task orchestration, and complex workflow automation.\n\n## \u2728 Key Features\n\n- \ud83d\ude80 **Fast & Lightweight** - Minimal dependencies, optimized for performance\n- \ud83d\udd04 **DAG & FSM Support** - Build both directed acyclic graphs and state machines\n- \ud83c\udfaf **Type-Safe** - Full type hints and mypy support\n- \ud83d\udd0c **Extensible** - Easy to add custom nodes and behaviors\n- \ud83d\udcca **Visualization** - Built-in Mermaid and Graphviz support\n- \u26a1 **Multiple Execution Modes** - Sequential, parallel, and async execution\n- \ud83d\udee1\ufe0f **Robust Error Handling** - Comprehensive error strategies and retry logic\n- \ud83d\udd0d **Runtime Validation** - Automatic cycle detection and dependency validation\n\n## \ud83d\udce6 Installation\n\n### Basic Installation\n\n```bash\npip install fast-dag\n```\n\n### With Visualization Support\n\n```bash\npip install fast-dag[viz]\n```\n\n### With All Features\n\n```bash\npip install fast-dag[all]\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/felixnext/fast-dag.git\ncd fast-dag\npip install -e \".[dev]\"\n```\n\n## \ud83d\ude80 Quick Start\n\n### Simple DAG Example\n\n```python\nfrom fast_dag import DAG\n\n# Create a DAG\ndag = DAG(\"data_pipeline\")\n\n# Define nodes using decorators\n@dag.node\ndef fetch_data() -> dict:\n    return {\"data\": [1, 2, 3, 4, 5]}\n\n@dag.node\ndef process_data(data: dict) -> list:\n    return [x * 2 for x in data[\"data\"]]\n\n@dag.node\ndef save_results(processed: list) -> str:\n    return f\"Saved {len(processed)} items\"\n\n# Connect nodes\ndag.connect(\"fetch_data\", \"process_data\", input=\"data\")\ndag.connect(\"process_data\", \"save_results\", input=\"processed\")\n\n# Execute the DAG\nresult = dag.run()\nprint(result)  # \"Saved 5 items\"\n```\n\n### FSM Example\n\n```python\nfrom fast_dag import FSM\nfrom fast_dag.core.types import FSMReturn\n\n# Create a finite state machine\nfsm = FSM(\"traffic_light\")\n\n@fsm.state(initial=True)\ndef red() -> FSMReturn:\n    print(\"Red light - STOP\")\n    return FSMReturn(next_state=\"green\")\n\n@fsm.state\ndef green() -> FSMReturn:\n    print(\"Green light - GO\")\n    return FSMReturn(next_state=\"yellow\")\n\n@fsm.state\ndef yellow() -> FSMReturn:\n    print(\"Yellow light - SLOW DOWN\")\n    return FSMReturn(next_state=\"red\")\n\n# Run for 5 cycles\nfsm.max_cycles = 5\nfsm.run()\n```\n\n## \ud83c\udfaf Core Concepts\n\n### Nodes\n\nNodes are the basic units of work in fast-dag:\n\n```python\n@dag.node\ndef my_task(x: int) -> int:\n    return x * 2\n\n# Or create nodes manually\nfrom fast_dag.core.node import Node\n\nnode = Node(\n    func=my_function,\n    name=\"my_node\",\n    description=\"Process data\"\n)\n```\n\n### Connections\n\nConnect nodes to define data flow:\n\n```python\n# Simple connection\ndag.connect(\"source\", \"target\")\n\n# Specify input/output names\ndag.connect(\"source\", \"target\", output=\"result\", input=\"data\")\n\n# Use operator syntax\ndag.nodes[\"a\"] >> dag.nodes[\"b\"] >> dag.nodes[\"c\"]\n```\n\n### Conditional Flows\n\nBuild dynamic workflows with conditions:\n\n```python\n@dag.condition\ndef check_threshold(value: int) -> bool:\n    return value > 100\n\n@dag.node\ndef process_high(value: int) -> str:\n    return f\"High value: {value}\"\n\n@dag.node\ndef process_low(value: int) -> str:\n    return f\"Low value: {value}\"\n\n# Connect conditional branches\ndag.nodes[\"check_threshold\"].on_true >> dag.nodes[\"process_high\"]\ndag.nodes[\"check_threshold\"].on_false >> dag.nodes[\"process_low\"]\n```\n\n### Advanced Node Types\n\n```python\n# Multi-input convergence\n@dag.any()  # Waits for ANY input to be ready\ndef merge_first(data: dict) -> str:\n    return f\"Received: {data}\"\n\n@dag.all()  # Waits for ALL inputs to be ready\ndef merge_all(data: dict) -> str:\n    return f\"All data: {data}\"\n\n# Multi-way branching\n@dag.select\ndef router(request: dict) -> SelectReturn:\n    category = request.get(\"category\", \"default\")\n    return SelectReturn(branch=category, value=request)\n```\n\n## \ud83d\udd27 Advanced Features\n\n### Execution Modes\n\n```python\n# Sequential execution (default)\nresult = dag.run()\n\n# Parallel execution\nresult = dag.run(mode=\"parallel\")\n\n# Async execution\nresult = await dag.run_async()\n\n# Custom runner configuration\nfrom fast_dag.runner import DAGRunner\n\nrunner = DAGRunner(dag)\nrunner.configure(\n    max_workers=4,\n    timeout=300,\n    error_strategy=\"continue\"\n)\nresult = runner.run()\n```\n\n### Error Handling\n\n```python\n# Retry with exponential backoff\n@dag.node(retry=3, retry_delay=1.0)\ndef flaky_operation() -> dict:\n    # Will retry up to 3 times with exponential backoff\n    return fetch_external_data()\n\n# Error strategies\nresult = dag.run(error_strategy=\"continue\")  # Continue on error\nresult = dag.run(error_strategy=\"stop\")      # Stop on first error (default)\n```\n\n### Node Lifecycle Hooks\n\n```python\ndef before_node(node, inputs):\n    print(f\"Starting {node.name}\")\n\ndef after_node(node, inputs, result):\n    print(f\"Finished {node.name}: {result}\")\n    return result  # Can modify result\n\ndef on_error(node, inputs, error):\n    print(f\"Error in {node.name}: {error}\")\n\ndag.set_node_hooks(\n    \"my_node\",\n    pre_execute=before_node,\n    post_execute=after_node,\n    on_error=on_error\n)\n```\n\n### Visualization\n\n```python\n# Generate Mermaid diagram\nmermaid_code = dag.visualize(backend=\"mermaid\")\n\n# Generate Graphviz DOT\ndot_code = dag.visualize(backend=\"graphviz\")\n\n# Visualize with execution results\ndag.run()\nfrom fast_dag.visualization import VisualizationOptions\n\noptions = VisualizationOptions(\n    show_results=True,\n    direction=\"LR\",\n    success_color=\"#90EE90\",\n    error_color=\"#FFB6C1\"\n)\ndag.visualize(options=options, filename=\"pipeline\", format=\"png\")\n```\n\n### Context and Metrics\n\n```python\n# Access context during execution\n@dag.node\ndef process_with_context(data: dict, context: Context) -> dict:\n    # Access previous results\n    previous = context.get(\"previous_node\")\n    \n    # Store metadata\n    context.metadata[\"process_time\"] = time.time()\n    \n    return {\"combined\": [data, previous]}\n\n# After execution, access metrics\ndag.run()\nprint(dag.context.metrics[\"execution_order\"])\nprint(dag.context.metrics[\"node_times\"])\nprint(dag.context.metadata)\n```\n\n## \ud83d\udccb More Examples\n\n### Data Processing Pipeline\n\n```python\ndag = DAG(\"etl_pipeline\")\n\n@dag.node\ndef extract_data() -> pd.DataFrame:\n    return pd.read_csv(\"data.csv\")\n\n@dag.node\ndef transform_data(df: pd.DataFrame) -> pd.DataFrame:\n    return df.dropna().reset_index(drop=True)\n\n@dag.node\ndef validate_data(df: pd.DataFrame) -> pd.DataFrame:\n    assert len(df) > 0, \"No data after transformation\"\n    return df\n\n@dag.node\ndef load_data(df: pd.DataFrame) -> str:\n    df.to_parquet(\"output.parquet\")\n    return f\"Loaded {len(df)} rows\"\n\n# Connect the pipeline\n(\n    dag.nodes[\"extract_data\"] \n    >> dag.nodes[\"transform_data\"] \n    >> dag.nodes[\"validate_data\"] \n    >> dag.nodes[\"load_data\"]\n)\n\nresult = dag.run()\n```\n\n### State Machine with Conditions\n\n```python\nfsm = FSM(\"order_processor\")\n\n@fsm.state(initial=True)\ndef pending(order_id: str) -> FSMReturn:\n    # Process payment\n    if payment_successful():\n        return FSMReturn(next_state=\"confirmed\", value={\"order_id\": order_id})\n    else:\n        return FSMReturn(next_state=\"failed\", value={\"reason\": \"payment_failed\"})\n\n@fsm.state\ndef confirmed(data: dict) -> FSMReturn:\n    # Ship order\n    tracking = ship_order(data[\"order_id\"])\n    return FSMReturn(next_state=\"shipped\", value={\"tracking\": tracking})\n\n@fsm.state(terminal=True)\ndef shipped(data: dict) -> FSMReturn:\n    notify_customer(data[\"tracking\"])\n    return FSMReturn(stop=True, value=\"Order delivered\")\n\n@fsm.state(terminal=True)\ndef failed(data: dict) -> FSMReturn:\n    log_failure(data[\"reason\"])\n    return FSMReturn(stop=True, value=\"Order failed\")\n\nresult = fsm.run(order_id=\"12345\")\n```\n\n## \ud83d\udee0\ufe0f Development\n\n### Running Tests\n\n```bash\n# Run all tests\nuv run pytest\n\n# Run with coverage\nuv run pytest --cov=fast-dag\n\n# Run specific test file\nuv run pytest tests/unit/test_dag.py\n```\n\n### Code Quality\n\n```bash\n# Format code\nuv run ruff format .\n\n# Lint code\nuv run ruff check . --fix\n\n# Type checking\nuv run mypy fast-dag\n```\n\n### Pre-commit Hooks\n\n```bash\n# Install pre-commit hooks\nuv run pre-commit install\n\n# Run manually\nuv run pre-commit run --all-files\n```\n\n### Building and Publishing\n\n```bash\n# Build the package\nuv run python -m build\n\n# Check the distribution\nuv run twine check dist/*\n\n# Upload to TestPyPI (for testing)\nuv run twine upload --repository testpypi dist/*\n\n# Upload to PyPI\nuv run twine upload dist/*\n```\n\n## \ud83d\udcda Documentation\n\nFor more detailed documentation, examples, and API reference, visit our [documentation site](https://github.com/felixnext/fast-dag/wiki).\n\n## \ud83e\udd1d Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## \ud83d\ude4f Acknowledgments\n\n- Built with [UV](https://github.com/astral-sh/uv) for fast, reliable Python package management\n- Inspired by [Airflow](https://airflow.apache.org/), [Prefect](https://www.prefect.io/), and [Dagster](https://dagster.io/)\n- Special thanks to all contributors and users\n\n## \ud83d\udce7 Contact\n\n- **Author**: Felix Geilert\n- **GitHub**: [@felixnext](https://github.com/felixnext)\n- **PyPI**: [fast-dag](https://pypi.org/project/fast-dag/)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Add your description here",
    "version": "0.1.0",
    "project_urls": null,
    "split_keywords": [
        "dag",
        " workflow",
        " workflow-engine"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "184d7e6fe64632486483aeed53c50eb4b48c5d46b6df8b1311ec8a80c52036ec",
                "md5": "5b849e98ceb364b88df3d904dfbc91ae",
                "sha256": "0b7a3d1368037e74e529bba1a17b3a40fe668c2b2c87e0e71bd535781eed59b0"
            },
            "downloads": -1,
            "filename": "fast_dag-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5b849e98ceb364b88df3d904dfbc91ae",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 45060,
            "upload_time": "2025-07-16T21:08:36",
            "upload_time_iso_8601": "2025-07-16T21:08:36.205697Z",
            "url": "https://files.pythonhosted.org/packages/18/4d/7e6fe64632486483aeed53c50eb4b48c5d46b6df8b1311ec8a80c52036ec/fast_dag-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e01ecb1d488df8b3066158d9a9d6274623da538e8fbce44e8320cf74ddfe3fe6",
                "md5": "9a12fd1e651886ba0914f7f8f2c369b8",
                "sha256": "3024ca75fe003308d584cb4bb843ef460e1d06e1e84fecb0d131d0b972cb7063"
            },
            "downloads": -1,
            "filename": "fast_dag-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "9a12fd1e651886ba0914f7f8f2c369b8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 145196,
            "upload_time": "2025-07-16T21:08:37",
            "upload_time_iso_8601": "2025-07-16T21:08:37.372694Z",
            "url": "https://files.pythonhosted.org/packages/e0/1e/cb1d488df8b3066158d9a9d6274623da538e8fbce44e8320cf74ddfe3fe6/fast_dag-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-16 21:08:37",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "fast-dag"
}
        
Elapsed time: 0.76067s