Python Workers zijn nu algemeen beschikbaar

Twee jaar geleden introduceerden we Python Workers, waarmee Python-applicaties kunnen worden uitgevoerd in de Cloudflare Workers-runtime. Ons doel was om het schrijven van Workers in Python net zo eenvoudig te maken als in TypeScript, en om ervoor te zorgen dat het ecosysteem van Python-pakketten en frameworks "gewoon werkt".

Vandaag zijn Python Workers algemeen beschikbaar (General Availability, GA).

Wat betekent GA?

Het betekent dat Python nu een eersteklas, volledig ondersteunde taal is op het Cloudflare Developer Platform. Je kunt de Python-code, bibliotheken en ontwerppatronen die je al kent gebruiken en deze naadloos verbinden met Workers AI, R2, D1, Hyperdrive, Durable Objects, Queues, Workflows en de rest van het Cloudflare-platform. Daarnaast kun je populaire Python-frameworks zoals FastAPI, Django en Flask draaien binnen Python Workers. Met Dynamic Workers kun je zelfs een Python Worker binnen een andere Worker maken.

from fastapi import FastAPI, Request
from workers import asgi, WorkerEntrypoint

app = FastAPI()

@app.get("/")
async def root(request: Request):
    env = request.scope["env"]
    return await env.AI.run(
        "@cf/openai/gpt-oss-120b",
        {
            "instructions": "You are a friendly assistant.",
            "input": "What is the origin of the phrase Hello, World?",
        },
    )

Default = asgi.entrypoint(app)

De weg naar Python Workers

Het toevoegen van Python aan Cloudflare Workers was een logische keuze. Omdat Workers sinds 2018 WebAssembly (Wasm) ondersteunt, bood dit de perfecte omgeving om een Wasm-gecompileerde Python-interpreter uit te voeren. Door gebruik te maken van Pyodide konden we snel een breed scala aan Python-applicaties ondersteunen.

Ons doel was om het eerste platform voor oneindig schaalbare Python-apps te creëren, terwijl het ontwikkelproces net zo eenvoudig en performant blijft als bij Python-apps elders. De functies die we vandaag presenteren, zijn het resultaat van deze meerjarige inspanning.

Python als eersteklas taal in de Cloudflare Workers-runtime

Python Workers ondersteunen nu native de bindings van het Cloudflare Developer Platform. Voorheen was het nodig om Python-objecten expliciet om te zetten naar TypeScript-objecten op de RPC-grens. Bijvoorbeeld, het verzenden van een Python-dictionary naar een Cloudflare Queue vereiste de volgende "glue code":

from pyodide.ffi import to_js
import js
self.env.QUEUE.send(to_js({"key": "value"}, dict_converter=js.Object.fromEntries))

Dit dwong Python-ontwikkelaars om rekening te houden met de JavaScript-omgeving, wat vaak leidde tot fouten bij zowel mensen als AI-agents. We hebben dit proces nu volledig ingekapseld in de Workers-runtime en de Python SDK. Hierdoor kun je alle Cloudflare-bindings op een "Pythonic" manier gebruiken zonder een regel JavaScript te schrijven:

self.env.QUEUE.send({"key": "value"})

Webframeworks: FastAPI, Django en Flask

Je kunt nu je favoriete Python-framework, zoals FastAPI, Django of Flask, gebruiken om een API-server te bouwen in Python Workers. We hebben een ingebouwde connector geïmplementeerd om je webapplicatie eenvoudig te verbinden.

Stel dat je een eenvoudige FastAPI-applicatie hebt:

from fastapi import FastAPI
app = FastAPI()

@app.get("/")
async def root():
    message = "Hello, world!"
    return {"message": message}

In een traditionele omgeving zou je een webserver zoals uvicorn gebruiken: $ uvicorn main:app

In Python Workers kun je dezelfde applicatie draaien met het workers.asgi pakket door dit fragment toe te voegen:

from workers import asgi

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return await asgi.fetch(app, request, self.env)

# Of gelijkwaardig:
Default = asgi.entrypoint(app)

Voor synchrone webapplicaties zoals Django kun je het workers.wsgi pakket gebruiken:

from workers import WorkerEntrypoint, wsgi
from your_django_app.wsgi import app

Default = wsgi.entrypoint(app)

Hoe werkt dit onder de motorkap?

Python hanteert standaarden voor de communicatie tussen webapplicaties en webservers: WSGI (Web Server Gateway Interface) en de moderne asynchrone variant ASGI. Deze standaarden maken applicaties server-agnostisch. In een traditionele deployment handelen servers zoals Uvicorn of Gunicorn de verbindingen en threads af, terwijl het framework zich richt op de applicatielogica.

Bij Cloudflare Workers fungeert het platform zelf als de webserver. Omdat ons wereldwijde netwerk load balancing en schaling al afhandelt, hoeven we geen server binnen de Python Worker te draaien. Onze workers.asgi en workers.wsgi connectors fungeren als een dunne, geoptimaliseerde brug. Ze vertalen inkomende native JavaScript-requests naar de standaard WSGI/ASGI-structuren die Python-applicaties verwachten, en sturen de respons terug met minimale overhead.

PostgreSQL en MySQL gebruiken met Hyperdrive

Voor applicaties die relationele databases zoals PostgreSQL of MySQL gebruiken, is integratie met Hyperdrive nu mogelijk.

Voorheen ondersteunden Python Workers geen TCP-sockets, waardoor database-drivers niet werkten. Python-drivers zoals aiomysql of asyncpg vertrouwen op de socket-module van de standaardbibliotheek. In een WebAssembly-sandbox zijn deze POSIX-netwerkoproepen normaal gesproken "stubs" die altijd falen. We hebben dit opgelost door socket-systeemaanroepen te implementeren via de Workers connect API.

Wanneer een driver een TCP-verbinding probeert te openen, vertaalt onze implementatie deze operaties naar de JavaScript-aanroepen van de Workers-runtime. Hierdoor hoeven de database-drivers niets te weten over de onderliggende implementatie.

Om Hyperdrive te gebruiken, configureer je eerst de binding in wrangler.toml:

"hyperdrive": [
  {
    "binding": "HYPERDRIVE_MYSQL",
    "id": "<example id: 57b7076f58be42419276f058a8968187>",
  }
]

Vervolgens verbind je met Hyperdrive via de bekende drivers:

import aiomysql
from workers import WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        hd = self.env.HYPERDRIVE_MYSQL
        conn = await aiomysql.connect(
            host=hd.host,
            port=int(hd.port),
            user=hd.user,
            password=hd.password,
            db=hd.database,
            ssl=None,
        )
        cur = await conn.cursor()
        await cur.execute("SELECT username FROM user")
        r = await cur.fetchall()
        await cur.close()
        conn.close()

Uitbreiding van het WebAssembly-pakketecosysteem

Omdat Python Workers in een Wasm-sandbox draaien, moeten pakketten met native C/C++/Rust-extensies worden gecompileerd naar WebAssembly. Voorheen was er geen standaard hiervoor, waardoor ons team pakketten handmatig moest compileren.

Om dit te verbeteren, hebben we PEP 783 voorgesteld, wat een platform standaardiseert voor het draaien van Python in browser-runtimes, genaamd PyEmscripten. Dit voorstel is geaccepteerd, waardoor pakketbeheerders nu pakketten kunnen bouwen voor het PyEmscripten-platform die overal werken waar PyEmscripten is geïmplementeerd.

Daarnaast hebben we de Pyodide-build toolchain gestabiliseerd en ondersteuning voor het PyEmscripten-platform toegevoegd aan cibuildwheel. Hoewel het ecosysteem nog in transitie is, hopen we dat elk Python-pakket in de toekomst een Wasm-compatibele "wheel" heeft.

AI-agents en pipelines bouwen in Python

Vanwege het ecosysteem voor data science en machine learning is Python de ideale keuze voor AI-agents. Een uitdaging was echter dat bibliotheken zoals openai en langchain vertrouwen op HTTP-clients zoals requests of httpx, die voorheen niet correct werkten door het ontbreken van low-level socket-ondersteuning.

We hebben upstream bijgedragen om ervoor te zorgen dat deze HTTP-clients requests direct via de JavaScript fetch API kunnen routeren in Wasm-omgevingen. Hierdoor werken AI-bibliotheken zoals openai, langchain en mcp nu native in Python Workers.

Voorbeeld van het draaien van Worker AI-modellen in langchain via het langchain-cloudflare pakket:

from langchain_cloudflare import ChatCloudflareWorkersAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        prompt = PromptTemplate.from_template(
            "In one sentence, describe a great day in the life of an {profession}."
        )
        llm = ChatCloudflareWorkersAI(
            model_name="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
            binding=self.env.AI,
            max_tokens=64,
        )
        chain = prompt | llm | StrOutputParser()
        result = await chain.ainvoke({"profession": "electrician"})
        return Response.json({"result": result})

Wat kun je vandaag bouwen?

In onze python-workers-examples repository hebben we productieklaare patronen verzameld:

  • Asynchrone AI-orchestratie: Een image-to-image generator die requests ontvangt, deze in een Cloudflare Queue plaatst, via Workflows de generatie orkestreert met Workers AI en het resultaat opslaat in een R2-bucket.
  • Real-time stream processing met Bluesky Jetstream: Gebruik van een Python Worker om verbinding te maken met de ATProto/Bluesky Jetstream WebSocket, ondersteund door een Durable Object om de verbinding in stand te houden.
  • Model Context Protocol (MCP) Server: Implementatie van een MCP-server met het officiële Python MCP-pakket om AI-assistenten toegang te geven tot edge-data.
  • Retrieval-Augmented Generation (RAG) systeem: Gebruik van Workers AI en Vectorize (de vector database van Cloudflare).

Documentatie en toekomst

We hebben onze documentatie bijgewerkt; bijna overal waar een TypeScript-voorbeeld staat, is nu ook een Python-voorbeeld beschikbaar. Je kunt in de documentatie schakelen tussen JavaScript, TypeScript en Python.

Het bereiken van GA is slechts het begin. Onze plannen voor de toekomst omvatten het verbeteren van de performance en het geheugengebruik van Python Workers, en het ondersteunen van nog meer pakketten.