Skip to main content

py-cloud-task

A Framework Agnostic Client for Google Cloud Tasks.
Move from "Pull" (Workers) to "Push" (Serverless) architecture effortlessly.

Tests Supported Python versions


py-cloud-task is a lightweight, async-first library that abstracts the complexity of Google Cloud Tasks. It provides a developer experience similar to Celery or TaskIQ but is designed specifically for Serverless environments (Cloud Run, App Engine, Cloud Functions, FastAPI).

It handles serialization, authentication (OIDC), scheduling, and—crucially—FastAPI Dependency Injection automatically.

Why use this instead of Celery/Redis?

Feature Celery / Redis (Pull) py-cloud-task (Push)
Architecture Workers poll Redis 24/7 ("Are there tasks?") Google calls your API via HTTP ("Here is a task")
Cost You pay for idle workers & Redis instances Pay-per-use (Scale to Zero supported)
Infra Requires Redis/RabbitMQ management Zero Ops (Managed by Google)
Retries Managed by worker code Native (Exponential backoff managed by GCP)
DX Heavy setup Decorator-based (Just like FastAPI)

Installation

Currently, the package is available via GitHub. You can install it using uv or pip.

# Instalação Core
uv add "py-cloud-task @ git+https://github.com/uhmiller/py-cloud-task.git"

# Com suporte a FastAPI
uv add "py-cloud-task[fastapi] @ git+https://github.com/uhmiller/py-cloud-task.git"

# Para simulação local (testes)
uv add "py-cloud-task[test] @ git+https://github.com/uhmiller/py-cloud-task.git"

Quick Start

1. Configure the Client

The CloudTaskClient is the entry point. It holds the configuration for your Google Cloud project and queue.

from cloudtask import CloudTaskClient

client = CloudTaskClient(
    project="my-gcp-project",
    location="europe-west1",
    queue="default",
    url="[https://api.myapp.com/tasks/run](https://api.myapp.com/tasks/run)",  # The public URL of your worker
    sae="my-service-account@my-gcp-project.iam.gserviceaccount.com",  # Service Account email for OIDC auth
    secret="super-secret-token",  # Optional: Header secret for extra security
    force_to_queue=None,  # Optional: Force all tasks to a specific queue (useful for Staging)
    eager=None,  # None = Production (Sends to Google Cloud)
    add_url_func_name_suffix=False # url = /tasks => /tasks/{task_func_name}
)

2. Define a Task

Use the @client.task decorator. You can define tasks anywhere in your code.

@client.task(queue='high-priority', name='unique-task-name')
async def send_welcome_email(user_id: str, email: str):
    print(f"Sending email to {email}...")
    # ... logic to send email ...
    return "sent"

3. Trigger the Task

You can trigger tasks asynchronously. This will serialize the arguments and send them to Google Cloud Tasks.

# Simple trigger
await send_welcome_email(user_id="123", email="user@example.com").push()

Advanced Usage

Scheduling (Delayed Execution)

Schedule a task to run in the future using the at parameter.

from datetime import datetime, timedelta

# Run 1 hour from now
eta = datetime.now() + timedelta(hours=1)

await send_welcome_email("123", "user@example.com").push(at=eta)

Task Deduplication (Named Tasks)

Google Cloud Tasks ensures that tasks with the same name are executed only once. You can set a custom name to prevent duplicate execution.

# Instantiate the task wrapper first
task = send_welcome_email("123", "user@example.com")

# Set a deterministic name (e.g., specific to the user and action)
task.name = "welcome-email-user-123"

# Push to cloud
await task.push()

Local Development (Eager Modes)

When developing locally, you often don't want to send tasks to Google Cloud. The eager parameter supports three modes to help you develop and test safely.

Mode 1: Immediate Execution (eager="immediate")

Runs the function directly in the current process. Fastest option for Unit Tests.

client = CloudTaskClient(..., eager="immediate")
await send_welcome_email("123", "user@example.com").push()
# Result: Function runs instantly. No HTTP. No Serialization.

Mode 2: Remote Simulation (eager="remote")

Simulates a full HTTP request to your local worker using httpx. This is perfect for Integration Tests because it validates serialization, headers, and dependency injection without needing Google infrastructure.

client = CloudTaskClient(..., eager="remote")
await send_welcome_email("123", "user@example.com").push()
# Result: Sends POST http://localhost:8000/tasks/run.

Mode 3: Production (eager=None)

The default behavior. Serializes the task and sends it to Google Cloud Tasks.


FastAPI Integration

py-cloud-task has first-class support for FastAPI. It leverages FastAPI's native Dependency Injection system.

1. Setup the Router

from fastapi import FastAPI
from cloudtask.fastapi import CloudTaskRouter
from app.core.tasks import client

app = FastAPI()

# Register the route that receives tasks from Google
app.include_router(CloudTaskRouter(client), prefix="/tasks")

2. Use Depends in Tasks

You can inject database sessions, services, or any other dependency directly into your tasks, just like in API endpoints.

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.db import get_db


@client.task()
async def process_order(
        order_id: int,
        db: AsyncSession = Depends(get_db)  # <--- Magic happens here
):
    # The 'db' session is created, injected, and closed automatically!
    order = await db.get(Order, order_id)
    order.status = "processed"
    await db.commit()

Note: When triggering the task, you only pass the data arguments. The dependencies are resolved automatically by the worker.

# Correct usage (Dependency is ignored during push)
await process_order(order_id=500).push()

Security

To ensure that only Google Cloud Tasks can call your worker endpoint, the library supports two mechanisms:

  1. OIDC Token (Recommended): The library automatically attaches an OIDC token identifying the Service Account. Your Cloud Run/Functions service should validate this token (Google handles this automatically for Cloud Run if you don't allow unauthenticated invocations).
  2. Secret Header: You can configure a shared secret.
client = CloudTaskClient(..., secret="my-secret-key")

The router will automatically validate the X-PYCT-SECRET header and reject unauthorized requests (403 Forbidden).


Contributing

Contributions are welcome! If you find a bug or want to add a feature (e.g., Flask or Django adapters), please open an issue or submit a PR.


Built with ❤️ by the engineering team at Ziett

Ziett Logo

Metadata

Release files for py-cloud-task 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 py-cloud-task 0.2.1
File Size Uploaded Provenance Actions
py_cloud_task-0.2.1.tar.gz 11.4 kB Without provenance Details for py_cloud_task-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for py-cloud-task 0.2.1
File Interpreter ABI Platform Actions
py_cloud_task-0.2.1-py3-none-any.whl Python 3 none any Details for py_cloud_task-0.2.1-py3-none-any.whl

Total release size: 25.4 kB

Release files / py_cloud_task-0.2.1.tar.gz

Download URL py_cloud_task-0.2.1.tar.gz
Size 11.4 kB
Tags Source
SHA-256 checksum
How to use checksums
1bb4aeef869ba18e85f0cf6eaeb39b17c4d734b8760ec8416c16902924681c78
BLAKE2b-256 checksum
How to use checksums
b03d456b4105c32779d9f0f9ff00a2b318b7935c1c851155cad3dc34e319c1e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

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

Download URL py_cloud_task-0.2.1-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c9243d0e07506061d7092847ac6cff7a022cb7ccc21e1dafe437018fc7ebb0c4
BLAKE2b-256 checksum
How to use checksums
78918887ad5390b8e9d7fabfabf399293774bb88a00c894e64950439d1f04bf5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for py-cloud-task 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