Frameworks

Own one generated Repost client across Django, Flask, or FastAPI and close it during graceful shutdown.

The Python package is framework-neutral. Create one generated client for each application process, inject or import it where events are published, and close it during shutdown. Do not construct a client for every request: the shared instance owns bounded connection pools and runtime state.

Django

Keep the client in a small server-only module and import it from views, services, or signal handlers:

import atexit
 
from repost_sdk import RepostClient
 
repost = RepostClient()
atexit.register(repost.close)

atexit is a process fallback. When your WSGI or ASGI server exposes worker lifecycle hooks, create and close the client in those hooks instead. Each worker process owns its own instance.

If AppConfig.ready() wires Django signals, guard against the development autoreloader registering them twice. Do not perform sends or other network work from ready().

Flask

Create the client next to the application, then expose it through app.extensions for handlers and services:

import atexit
 
from flask import Flask
from repost_sdk import RepostClient
 
 
def create_app() -> Flask:
    app = Flask(__name__)
    repost = RepostClient()
    app.extensions["repost"] = repost
    atexit.register(repost.close)
    return app

Prefer your serving process's graceful-shutdown hook when it provides one. Flask request teardown callbacks run after every request, so they are the wrong owner for a process-wide client.

FastAPI

Python sends are synchronous in 0.4. Own the client with the application lifespan and call generated methods through Starlette's thread pool so an async route does not block the event loop:

from contextlib import asynccontextmanager
 
from fastapi import FastAPI
from starlette.concurrency import run_in_threadpool
 
from repost_sdk import RepostClient, User
 
 
@asynccontextmanager
async def lifespan(app: FastAPI):
    repost = RepostClient()
    app.state.repost = repost
    try:
        yield
    finally:
        repost.close()
 
 
app = FastAPI(lifespan=lifespan)
 
 
@app.post("/users")
async def create_user() -> dict[str, str]:
    result = await run_in_threadpool(
        app.state.repost.webhooks.user.created,
        customer_id="customer_123",
        data=User(id="user_123", email="[email protected]"),
        idempotency_key="user_123:created",
    )
    return {"message_id": result.id}

The SDK does not create an event loop or install an executor. Your framework remains responsible for thread-pool capacity and request cancellation policy.

Trace parenting

Configure telemetry() once when you construct the client. It captures the current OpenTelemetry context for every send, so repost.send becomes a child of the active inbound request span. Standard Django, Flask, and FastAPI OpenTelemetry middleware can provide that parent. The SDK does not install middleware or global instrumentation.

Continue