> ## Documentation Index
> Fetch the complete documentation index at: https://taskdaemon.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

# Python SDK

The Python SDK provides a clean interface for writing TaskDaemon handlers.

## Installation

```bash theme={null}
pip install taskdaemon
```

## Basic Usage

```python theme={null}
from taskdaemon import run, Task, Success, Error

def handler(task: Task) -> Success | Error:
    name = task.task_data.get("name", "World")
    return Success({"message": f"Hello, {name}!"})

run(handler)
```

## Task Object

The `Task` dataclass contains:

```python theme={null}
@dataclass
class Task:
    task_id: str      # Unique task identifier
    task_type: str    # Handler type (e.g., "resize")
    task_data: dict   # Input data from client
    attempt: int      # Current attempt number (starts at 0)
```

## Return Types

### Success

Return `Success` with a result (any JSON-serializable value):

```python theme={null}
return Success({"processed": True, "count": 42})
```

### Error

Return `Error` with an error message and optional retryable flag:

```python theme={null}
# Non-retryable error (default)
return Error("Invalid input: missing required field")

# Retryable error (will be retried)
return Error("Service temporarily unavailable", retryable=True)
```

## Complete Example

Here's the actual image handler from the sample project:

```python theme={null}
import base64
from io import BytesIO
import requests
from PIL import Image
from taskdaemon import run, Task, Success, Error

def handler(task: Task) -> Success | Error:
    data = task.task_data
    
    # Download image
    resp = requests.get(data["image_url"], timeout=30)
    img = Image.open(BytesIO(resp.content))
    
    if task.task_type == "resize":
        img = img.resize((data["width"], data["height"]), Image.LANCZOS)
    elif task.task_type == "thumbnail":
        size = data["size"]
        img.thumbnail((size, size), Image.LANCZOS)
    
    # Encode as base64
    buffer = BytesIO()
    img.save(buffer, format="PNG")
    b64 = base64.b64encode(buffer.getvalue()).decode()
    
    return Success({
        "size": [img.width, img.height],
        "format": "png",
        "data": b64
    })

if __name__ == "__main__":
    run(handler)
```

## Error Handling

Exceptions are automatically caught and converted to non-retryable errors:

```python theme={null}
def handler(task: Task) -> Success | Error:
    # If this raises, it becomes Error(str(exception), retryable=False)
    value = task.task_data["required_field"]
    return Success({"value": value})
```

For explicit error handling:

```python theme={null}
def handler(task: Task) -> Success | Error:
    if "required_field" not in task.task_data:
        return Error("Missing required_field")
    
    try:
        result = external_api_call()
        return Success(result)
    except ConnectionError:
        return Error("API unavailable", retryable=True)
    except ValueError as e:
        return Error(f"Invalid data: {e}")
```

## Dockerfile

```dockerfile theme={null}
FROM python:3.11-slim

RUN pip install taskdaemon pillow requests

COPY handler.py /app/
WORKDIR /app

CMD ["python", "-u", "handler.py"]
```

:::tip
Use `python -u` for unbuffered output to ensure responses are sent immediately.
:::

## Handler Configuration

```toml theme={null}
[handlers.resize]
image = "image-handler:latest"
instances = 4
timeout = 30

[handlers.thumbnail]
image = "image-handler:latest"
instances = 2
timeout = 15
```

Multiple task types can use the same image - the handler receives `task.task_type` to differentiate.
