diff --git a/src/content/docs/workers/configuration/cron-triggers.mdx b/src/content/docs/workers/configuration/cron-triggers.mdx index 029d57ad3d19b65..a3e33d6d3a1e3f9 100644 --- a/src/content/docs/workers/configuration/cron-triggers.mdx +++ b/src/content/docs/workers/configuration/cron-triggers.mdx @@ -5,7 +5,13 @@ head: [] description: Enable your Worker to be executed on a schedule. --- -import { Render, WranglerConfig, TabItem, Tabs, DashButton } from "~/components"; +import { + Render, + WranglerConfig, + TabItem, + Tabs, + DashButton, +} from "~/components"; ## Background @@ -55,11 +61,11 @@ export default { ```python -from workers import handler +from workers import WorkerEntrypoint, Response -@handler -async def on_scheduled(controller, env, ctx): - print("cron processed") +class Default(WorkerEntrypoint): + async def scheduled(self, controller, env, ctx): + print("cron processed") ``` @@ -143,31 +149,24 @@ To avoid ambiguity you may prefer to use the three-letter abbreviations (e.g. `S Some common time intervals that may be useful for setting up your Cron Trigger: - `* * * * *` - - At every minute - `*/30 * * * *` - - At every 30th minute - `45 * * * *` - - On the 45th minute of every hour - `0 17 * * sun` or `0 17 * * 1` - - 17:00 (UTC) on Sunday - `10 7 * * mon-fri` or `10 7 * * 2-6` - - 07:10 (UTC) on weekdays - `0 15 1 * *` - - 15:00 (UTC) on first day of the month - `0 18 * * 6L` or `0 18 * * friL` - - 18:00 (UTC) on the last Friday of the month - `59 23 LW * *` diff --git a/src/content/docs/workers/examples/cron-trigger.mdx b/src/content/docs/workers/examples/cron-trigger.mdx index 5cd1e7fd30f52fc..00e511181e84677 100644 --- a/src/content/docs/workers/examples/cron-trigger.mdx +++ b/src/content/docs/workers/examples/cron-trigger.mdx @@ -1,5 +1,4 @@ --- - summary: Set a Cron Trigger for your Worker. tags: - Middleware @@ -35,30 +34,30 @@ export default { ```ts interface Env {} export default { - async scheduled( - controller: ScheduledController, - env: Env, - ctx: ExecutionContext, - ) { - console.log("cron processed"); - }, + async scheduled( + controller: ScheduledController, + env: Env, + ctx: ExecutionContext, + ) { + console.log("cron processed"); + }, }; ``` ```python -from workers import handler +from workers import WorkerEntrypoint, Response -@handler -async def on_scheduled(controller, env, ctx): - print("cron processed") +class Default(WorkerEntrypoint): + async def scheduled(self, controller, env, ctx): + print("cron processed") ``` ```ts -import { Hono } from 'hono'; +import { Hono } from "hono"; interface Env {} @@ -66,27 +65,27 @@ interface Env {} const app = new Hono<{ Bindings: Env }>(); // Regular routes for normal HTTP requests -app.get('/', (c) => c.text('Hello World!')); +app.get("/", (c) => c.text("Hello World!")); // Export both the app and a scheduled function export default { - // The Hono app handles regular HTTP requests - fetch: app.fetch, - - // The scheduled function handles Cron triggers - async scheduled( - controller: ScheduledController, - env: Env, - ctx: ExecutionContext, - ) { - console.log("cron processed"); - - // You could also perform actions like: - // - Fetching data from external APIs - // - Updating KV or Durable Object storage - // - Running maintenance tasks - // - Sending notifications - }, + // The Hono app handles regular HTTP requests + fetch: app.fetch, + + // The scheduled function handles Cron triggers + async scheduled( + controller: ScheduledController, + env: Env, + ctx: ExecutionContext, + ) { + console.log("cron processed"); + + // You could also perform actions like: + // - Fetching data from external APIs + // - Updating KV or Durable Object storage + // - Running maintenance tasks + // - Sending notifications + }, }; ``` diff --git a/src/content/docs/workers/languages/python/basics.mdx b/src/content/docs/workers/languages/python/basics.mdx new file mode 100644 index 000000000000000..d0404167a59d698 --- /dev/null +++ b/src/content/docs/workers/languages/python/basics.mdx @@ -0,0 +1,169 @@ +--- +pcx_content_type: concept +title: The Basics +sidebar: + order: 4 +head: + - tag: title + content: Learn the basics of Python Workers +description: Learn the basics of Python Workers +--- + +import { WranglerConfig } from "~/components"; + +## Fetch Handler + +As mentioned in the [introduction to Python Workers](/workers/languages/python/), a Python Worker can be as simple as four lines of code: + +```python +from workers import WorkerEntrypoint, Response + +class Default(WorkerEntrypoint): + async def fetch(self, request): + return Response("Hello World!") +``` + +Similar to other Workers, the main entry point for a Python worker is the [`fetch` handler](/workers/runtime-apis/handlers/fetch) which handles incoming requests +sent to the Worker. + +In a Python Worker, this handler is placed in a `Default` class that extends the `WorkerEntrypoint` class (which you can import from the `workers` SDK module). + +## The `Request` Interface + +The `request` parameter passed to your `fetch` handler is a JavaScript Request object, exposed via the [foreign function interface (FFI)](/workers/languages/python/ffi), +allowing you to access it directly from your Python code. + +Let's try editing the worker to accept a POST request. We know from the +[documentation for `Request`](/workers/runtime-apis/request) that we can call +`await request.json()` within an `async` function to parse the request body as +JSON. + +In a Python Worker, you would write: + +```python +from workers import WorkerEntrypoint, Response +from hello import hello + +class Default(WorkerEntrypoint): + async def fetch(self, request): + name = (await request.json()).name + return Response(hello(name)) +``` + +Many other JavaScript APIs are available in Python Workers via the FFI, so you can +call other methods in a similar way. + +Once you edit the `src/entry.py`, Wrangler will automatically restart the local +development server. + +Now, if you send a POST request with the appropriate body, +your Worker will respond with a personalized message. + +```bash +curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"name": "Python"}' http://localhost:8787 +``` + +```bash output +Hello, Python! +``` + +## The `env` Attribute + +The `env` attribute on the `WorkerEntrypoint` can be used to access +[environment variables](/workers/configuration/environment-variables/), +[secrets](/workers/configuration/secrets/),and +[bindings](/workers/runtime-apis/bindings/). + +For example, let us try setting and using an environment variable in a Python Worker. First, add the environment variable to your Worker's [Wrangler configuration file](/workers/wrangler/configuration/): + + + +```toml +name = "hello-python-worker" +main = "src/entry.py" +compatibility_flags = ["python_workers"] +compatibility_date = "2025-11-02" + +[vars] +API_HOST = "example.com" +``` + + + +Then, you can access the `API_HOST` environment variable via the `env` parameter: + +```python +from workers import WorkerEntrypoint, Response + +class Default(WorkerEntrypoint): + async def fetch(self, request): + return Response(self.env.API_HOST) +``` + +## Modules + +Python workers can be split across multiple files. + +Let's create a new Python file, called `src/hello.py`: + +```python +def hello(name): + return "Hello, " + name + "!" +``` + +Now, we can modify `src/entry.py` to make use of the new module. + +```python +from hello import hello +from workers import WorkerEntrypoint, Response + +class Default(WorkerEntrypoint): + async def fetch(self, request): + return Response(hello("World")) +``` + +Once you edit `src/entry.py`, [`pywrangler`](/workers/languages/python/#the-pywrangler-cli-tool) will automatically detect the change and +reload your Worker. + +## Types and Autocompletion + +When developing Python Workers, you can take advantage of type hints and autocompletion +in your IDE. + +To enable them, install the `workers-runtime-sdk` package in your `pyproject.toml` file. + +```toml +[dependency-groups] +dev = [ + "workers-py", + "workers-runtime-sdk" +] +``` + +Additionally, you can generate types based on your Worker configuration using `uv run pywrangler types` + +This includes Env types based on your bindings, module rules, and runtime types based on the compatibility_date +and compatibility_flags in your config file. + +## Upgrading `pywrangler` + +To upgrade to the latest version of [`pywrangler`](/workers/languages/python/#the-pywrangler-cli-tool) globally, run the following command: + +```bash +uv tool upgrade workers-py +``` + +To upgrade to the latest version of `pywrangler` in a specific project, run the following command: + +```bash +uv lock --upgrade-package workers-py +``` + +## Next Up + +- Learn details about local development, deployment, and [how Python Workers work](/workers/languages/python/how-python-workers-work). +- Explore the [package](/workers/languages/python/packages) docs for instructions on how to use packages with Python Workers. +- Understand which parts of the [Python Standard Library](/workers/languages/python/stdlib) are supported in Python Workers. +- Learn about Python Workers' [foreign function interface (FFI)](/workers/languages/python/ffi), and how to use it to work with [bindings](/workers/runtime-apis/bindings) and [Runtime APIs](/workers/runtime-apis/). diff --git a/src/content/docs/workers/languages/python/examples.mdx b/src/content/docs/workers/languages/python/examples.mdx index 204d53fa6a286c2..5804d36e5006e40 100644 --- a/src/content/docs/workers/languages/python/examples.mdx +++ b/src/content/docs/workers/languages/python/examples.mdx @@ -4,7 +4,6 @@ title: Examples head: - tag: title content: Python Worker Examples - --- Cloudflare has a wide range of Python examples in the [Workers Example gallery](/workers/examples/?languages=Python). @@ -144,8 +143,77 @@ class Default(WorkerEntrypoint): Refer to [Query D1 from Python Workers](/d1/examples/query-d1-from-python-workers/) for a more in-depth tutorial that covers how to create a new D1 database and configure bindings to D1. -## Next steps +## Durable Object + +```python +from workers import WorkerEntrypoint, Response, DurableObject +from pyodide.ffi import to_js + +class List(DurableObject): + async def get_messages(self): + messages = await self.ctx.storage.get("messages") + return messages if messages else [] + + async def add_message(self, message): + messages = await self.get_messages() + messages.append(message) + await self.ctx.storage.put("messages", to_js(messages)) + return + + async def say_hello(self): + result = self.ctx.storage.sql.exec( + "SELECT 'Hello, World!' as greeting" + ).one() + + return result.greeting +``` + +Refer to [Durable Objects documentation](/durable-objects/get-started/) for more information. + +## Cron Trigger -* If you're new to Workers and Python, refer to the [get started](/workers/languages/python/) guide -* Learn more about [calling JavaScript methods and accessing JavaScript objects](/workers/languages/python/ffi/) from Python -* Understand the [supported packages and versions](/workers/languages/python/packages/) currently available to Python Workers. +```python +from workers import WorkerEntrypoint, Response + +class Default(WorkerEntrypoint): + async def scheduled(self, controller, env, ctx): + print("cron processed") +``` + +Refer to [Cron Triggers documentation](/workers/configuration/cron-triggers/) for more information. + +## Workflows + +```python +from workers import WorkflowEntrypoint + +class MyWorkflow(WorkflowEntrypoint): + async def run(self, event, step): + @step.do("dependency a") + async def step_a(): + # do some work + return 10 + + @step.do("dependency b") + async def step_b(): + # do some work + return 20 + + @step.do("my final step", depends=[step_a, step_b], concurrent=True) + async def my_final_step(result_a=0, result_b=0): + # should return 30 + return result_a + result_b + + await my_final_step() +``` + +Refer to the [Python Workflows documentation](/workflows/python/) for more information. + +## More Examples + +Or you can clone [the examples repository](https://github.com/cloudflare/python-workers-examples) to explore +even more examples: + +```bash +git clone https://github.com/cloudflare/python-workers-examples +``` diff --git a/src/content/docs/workers/languages/python/ffi.mdx b/src/content/docs/workers/languages/python/ffi.mdx index fcdcd5608d4e187..a8fef32001d4f3c 100644 --- a/src/content/docs/workers/languages/python/ffi.mdx +++ b/src/content/docs/workers/languages/python/ffi.mdx @@ -1,20 +1,21 @@ --- -pcx_content_type: reference +pcx_content_type: concept title: Foreign Function Interface (FFI) +sidebar: + order: 6 head: - tag: title content: Work with JavaScript objects, methods, functions and globals from Python Workers - --- import { WranglerConfig } from "~/components"; Via [Pyodide](https://pyodide.org/en/stable/), Python Workers provide a [Foreign Function Interface (FFI)](https://en.wikipedia.org/wiki/Foreign_function_interface) to JavaScript. This allows you to: -* Use [bindings](/workers/runtime-apis/bindings/) to resources on Cloudflare, including [Workers AI](/workers-ai/), [Vectorize](/vectorize/), [R2](/r2/), [KV](/kv/), [D1](/d1/), [Queues](/queues/), [Durable Objects](/durable-objects/), [Service Bindings](/workers/runtime-apis/bindings/service-bindings/) and more. -* Use JavaScript globals, like [`Request`](/workers/runtime-apis/request/), [`Response`](/workers/runtime-apis/response/), and [`fetch()`](/workers/runtime-apis/fetch/). -* Use the full feature set of Cloudflare Workers — if an API is accessible in JavaScript, you can also access it in a Python Worker, writing exclusively Python code. +- Use [bindings](/workers/runtime-apis/bindings/) to resources on Cloudflare, including [Workers AI](/workers-ai/), [Vectorize](/vectorize/), [R2](/r2/), [KV](/kv/), [D1](/d1/), [Queues](/queues/), [Durable Objects](/durable-objects/), [Service Bindings](/workers/runtime-apis/bindings/service-bindings/) and more. +- Use JavaScript globals, like [`Request`](/workers/runtime-apis/request/), [`Response`](/workers/runtime-apis/response/), and [`fetch()`](/workers/runtime-apis/fetch/). +- Use the full feature set of Cloudflare Workers — if an API is accessible in JavaScript, you can also access it in a Python Worker, writing exclusively Python code. The details of Pyodide's Foreign Function Interface are documented [here](https://pyodide.org/en/stable/usage/type-conversions.html), and Workers written in Python are able to take full advantage of this. @@ -49,6 +50,24 @@ class Default(WorkerEntrypoint): Under the hood, `env` is actually a JavaScript object. When you call `.FOO`, you are accessing this property via a [`JsProxy`](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy) — special proxy object that makes a JavaScript object behave like a Python object. +### Converting Python to JavaScript + +Occasionally, to interoperate with JavaScript APIs, you may need to convert a Python object to JavaScript. Pyodide provides a `to_js` function to facilitate this conversion. + +````python +from js import Object +from pyodide.ffi import to_js as _to_js + +from workers import WorkerEntrypoint, Response + +# to_js converts between Python dictionaries and JavaScript Objects +def to_js(obj): + return _to_js(obj, dict_converter=Object.fromEntries) + ``` +```` + +For more details, see out the [documentation on `pyodide.ffi.to_js`](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.to_js). + ## Using JavaScript globals from Python Workers When writing Workers in Python, you can access JavaScript globals by importing them from the `js` module. For example, note how `Response` is imported from `js` in the example below: diff --git a/src/content/docs/workers/languages/python/how-python-workers-work.mdx b/src/content/docs/workers/languages/python/how-python-workers-work.mdx index 3ad88d85c6ad2cd..b81a2a5298a6bcb 100644 --- a/src/content/docs/workers/languages/python/how-python-workers-work.mdx +++ b/src/content/docs/workers/languages/python/how-python-workers-work.mdx @@ -2,20 +2,21 @@ pcx_content_type: concept title: How Python Workers Work sidebar: - order: 1 + order: 5 head: - tag: title content: How Python Workers Work - --- -import { Render, WranglerConfig } from "~/components" +import { Render, WranglerConfig } from "~/components"; Workers written in Python are executed by [Pyodide](https://pyodide.org/en/stable/index.html). Pyodide is a port of [CPython](https://github.com/python) (the reference implementation of Python — commonly referred to as just "Python") to WebAssembly. When you write a Python Worker, your code is interpreted directly by Pyodide, within a V8 isolate. Refer to [How Workers works](/workers/reference/how-workers-works/) to learn more. -## Local Development Lifecycle +## Local Development + +A basic Python Worker includes a Python file with a `Default` class extending `WorkerEntrypoint`, such as: ```python from workers import Response, WorkerEntrypoint @@ -25,7 +26,7 @@ class Default(WorkerEntrypoint): return Response("Hello world!") ``` -…with a [Wrangler configuration file](/workers/wrangler/configuration/) that points to a .py file: +...and a [Wrangler configuration file](/workers/wrangler/configuration/) that points to this `.py` file: @@ -37,28 +38,27 @@ compatibility_date = "2024-04-01" -When you run `npx wrangler@latest dev` in local dev, the Workers runtime will: +When you run `uv run pywrangler dev` to do local dev, the Workers runtime will: 1. Determine which version of Pyodide is required, based on your compatibility date -2. Create a new v8 isolate for your Worker, and automatically inject Pyodide -3. Serve your Python code using Pyodide +2. Install any packages necessary based on your `pyproject.toml` file +3. Create a new v8 isolate for your Worker, and automatically inject Pyodide +4. Serve your Python code using Pyodide -There no extra toolchain or precompilation steps needed. The Python execution environment is provided directly by the Workers runtime, mirroring how Workers written in JavaScript work. +There are no extra toolchain or precompilation steps needed. The Python execution environment is provided directly by the Workers runtime, mirroring how Workers written in JavaScript work. Refer to the [Python examples](/workers/languages/python/examples/) to learn how to use Python within Workers. -## Deployment Lifecycle +## Deployment Lifecycle and Cold Start Optimizations -To reduce cold start times, when you deploy a Python Worker, Cloudflare performs as much of the expensive work as possible upfront, at deploy time. When you run npx `wrangler@latest deploy`, the following happens: +To reduce cold start times, when you deploy a Python Worker, Cloudflare performs as much of the expensive work as possible upfront, at deploy time. When you run npx `uv run pywrangler deploy`, the following happens: -1. Wrangler uploads your Python code and any vendored packages to the Workers API. -2. Cloudflare sends your Python code, and any vendored packages to the Workers runtime to be validated. -3. Cloudflare creates a new v8 isolate for your Worker, and automatically injects Pyodide plus any built-in packages requested by your bundle. +1. Wrangler uploads your Python code and any packages included in your `pyproject.toml` to the Workers API. +2. Cloudflare sends your Python code to the Workers runtime to be validated. +3. Cloudflare creates a new v8 isolate for your Worker, automatically injecting Pyodide. 4. Cloudflare scans the Worker’s code for import statements, execute them, and then take a snapshot of the Worker’s WebAssembly linear memory. Effectively, we perform the expensive work of importing packages at deploy time, rather than at runtime. 5. Cloudflare deploys this snapshot alongside your Worker’s Python code to the Cloudflare network. - - When a request comes in to your Worker, we load this snapshot and use it to bootstrap your Worker in an isolate, avoiding expensive initialization time: ![Diagram of how Python Workers are deployed to Cloudflare](~/assets/images/workers/languages/python/python-workers-deployment.png) diff --git a/src/content/docs/workers/languages/python/index.mdx b/src/content/docs/workers/languages/python/index.mdx index 1e39f33fe779124..c21034fbd135a03 100644 --- a/src/content/docs/workers/languages/python/index.mdx +++ b/src/content/docs/workers/languages/python/index.mdx @@ -1,6 +1,6 @@ --- pcx_content_type: navigation -title: Python +title: Python Workers sidebar: order: 3 badge: @@ -13,32 +13,20 @@ description: Write Workers in 100% Python import { WranglerConfig } from "~/components"; -Cloudflare Workers provides first-class support for Python, including support for: +Cloudflare Workers provides a first-class Python experience, including support for: -- The majority of Python's [Standard library](/workers/languages/python/stdlib/) -- All [bindings](/workers/runtime-apis/bindings/), including [Workers AI](/workers-ai/), [Vectorize](/vectorize), [R2](/r2), [KV](/kv), [D1](/d1), [Queues](/queues/), [Durable Objects](/durable-objects/), [Service Bindings](/workers/runtime-apis/bindings/service-bindings/) and more. -- [Environment Variables](/workers/configuration/environment-variables/), and [Secrets](/workers/configuration/secrets/) +- Easy to install and fast-booting [Packages](/workers/languages/python/packages), including [FastAPI](https://fastapi.tiangolo.com/), [Langchain](https://pypi.org/project/langchain/), [httpx](https://www.python-httpx.org/), [Pydantic](https://docs.pydantic.dev/latest/) and more. - A robust [foreign function interface (FFI)](/workers/languages/python/ffi) that lets you use JavaScript objects and functions directly from Python — including all [Runtime APIs](/workers/runtime-apis/) -- [Built-in packages](/workers/languages/python/packages), including [FastAPI](https://fastapi.tiangolo.com/), [Langchain](https://pypi.org/project/langchain/), [httpx](https://www.python-httpx.org/) and more. +- An ecosystem of services on the Workers Platform accessible via [bindings](/workers/runtime-apis/bindings/), including: + - State storage and databases like [KV](/kv), [D1](/d1), [Durable Objects](/durable-objects/) + - Access to [Environment Variables](/workers/configuration/environment-variables/), [Secrets](/workers/configuration/secrets/), and other Workers using [Service Bindings](/workers/runtime-apis/bindings/service-bindings/) + - AI capabilities with [Workers AI](/workers-ai/), [Vectorize](/vectorize) + - File storage with [R2](/r2) + - [Durable Workflows](/workflows/), [Queues](/queues/), and [ more](/workers/runtime-apis/bindings/) -:::caution[Python Workers are in beta.] - -You must add the `python_workers` compatibility flag to your Worker, while Python Workers are in open beta. Packages are supported using the [pywrangler](/workers/languages/python/packages) tool. - -We'd love your feedback. Join the #python-workers channel in the [Cloudflare Developers Discord](https://discord.cloudflare.com/) and let us know what you'd like to see next. -::: - -## Get started - -You need [uv](https://docs.astral.sh/uv/#installation) and Node. - -```bash -git clone https://github.com/cloudflare/python-workers-examples -cd python-workers-examples/01-hello -uv run pywrangler dev -``` +## Introduction -A Python Worker can be as simple as three lines of code: +A Python Worker can be as simple as four lines of code: ```python from workers import WorkerEntrypoint, Response @@ -48,15 +36,33 @@ class Default(WorkerEntrypoint): return Response("Hello World!") ``` -Similar to Workers written in [JavaScript](/workers/languages/javascript), [TypeScript](/workers/languages/typescript), or [Rust](/workers/languages/rust/), the main entry point for a Python worker is the [`fetch` handler](/workers/runtime-apis/handlers/fetch). In a Python Worker, this handler is placed in a `Default` class that extends the `WorkerEntrypoint` class (which you can import from the `workers` SDK module). +Similar to other Workers, the main entry point for a Python worker is the [`fetch` handler](/workers/runtime-apis/handlers/fetch) which handles incoming requests +sent to the Worker. + +In a Python Worker, this handler is placed in a `Default` class that extends the `WorkerEntrypoint` class (which you can import from the `workers` SDK module). + +:::caution[Python Workers are in beta.] + +You must add the `python_workers` compatibility flag to your Worker, while Python Workers are in open beta. Packages are supported using the [pywrangler](/workers/languages/python/packages) tool. + +We'd love your feedback. Join the #python-workers channel in the [Cloudflare Developers Discord](https://discord.cloudflare.com/) and let us know what you'd like to see next. +::: + +### The `pywrangler` CLI tool + +To run a Python Worker locally, install packages, and deploy it to Cloudflare, you use [pywrangler](https://github.com/cloudflare/workers-py), +the CLI for Python Workers. + +To set it up, first, ensure [uv](https://docs.astral.sh/uv/#installation) and [Node](https://nodejs.org/en) are installed. + +Then set up your development environment: -To run a Python Worker locally, you use pywrangler, the CLI for Python Workers. -To set it up, first you need to set up your development environment: ``` uv init uv tool install workers-py uv run pywrangler init ``` + This will create a `pyproject.toml` file with `workers-py` as a development dependency. `pywrangler init` will create a wrangler config file. You can then run `pywrangler` with: @@ -71,97 +77,25 @@ To deploy a Python Worker to Cloudflare, run `pywrangler deploy`: uv run pywrangler deploy ``` -## Modules +### Python Worker Templates -Python workers can be split across multiple files. Let's create a new Python file, called `src/hello.py`: - -```python -def hello(name): - return "Hello, " + name + "!" -``` - -Now, we can modify `src/entry.py` to make use of the new module. - -```python -from hello import hello -from workers import WorkerEntrypoint, Response - -class Default(WorkerEntrypoint): - async def fetch(self, request): - return Response(hello("World")) -``` - -Once you edit `src/entry.py`, Wrangler will automatically detect the change and -reload your Worker. - -## The `Request` Interface - -The `request` parameter passed to your `fetch` handler is a JavaScript Request object, exposed via the foreign function interface, allowing you to access it directly from your Python code. - -Let's try editing the worker to accept a POST request. We know from the -[documentation for `Request`](/workers/runtime-apis/request) that we can call -`await request.json()` within an `async` function to parse the request body as -JSON. In a Python Worker, you would write: - -```python -from workers import WorkerEntrypoint, Response -from hello import hello - -class Default(WorkerEntrypoint): - async def fetch(self, request): - name = (await request.json()).name - return Response(hello(name)) -``` - -Once you edit the `src/entry.py`, Wrangler should automatically restart the local -development server. Now, if you send a POST request with the appropriate body, -your Worker should respond with a personalized message. +When you initialize a new Python Worker project and select from one of many templates: ```bash -curl --header "Content-Type: application/json" \ - --request POST \ - --data '{"name": "Python"}' http://localhost:8787 -``` - -```bash output -Hello, Python! -``` - -## The `env` Attribute - -The `env` attribute on the ``WorkerEntrypoint`` can be used to access -[environment variables](/workers/configuration/environment-variables/), -[secrets](/workers/configuration/secrets/),and -[bindings](/workers/runtime-apis/bindings/). - -For example, let us try setting and using an environment variable in a Python Worker. First, add the environment variable to your Worker's [Wrangler configuration file](/workers/wrangler/configuration/): - - - -```toml -name = "hello-python-worker" -main = "src/entry.py" -compatibility_flags = ["python_workers"] -compatibility_date = "2024-03-20" - -[vars] -API_HOST = "example.com" +uv run pywrangler init ``` - - -Then, you can access the `API_HOST` environment variable via the `env` parameter: +Or you can clone the examples repository to explore more options: -```python -from workers import WorkerEntrypoint, Response - -class Default(WorkerEntrypoint): - async def fetch(self, request): - return Response(self.env.API_HOST) +```bash +git clone https://github.com/cloudflare/python-workers-examples +cd python-workers-examples/01-hello ``` -## Further Reading +## Next Up +- Learn more about [the basics of Python Workers](/workers/languages/python/basics) +- Learn details about local development, deployment, and [how to Python Workers work](/workers/languages/python/how-python-workers-work). +- Explore the [package](/workers/languages/python/packages) docs for instructions on how to use packages with Python Workers. - Understand which parts of the [Python Standard Library](/workers/languages/python/stdlib) are supported in Python Workers. - Learn about Python Workers' [foreign function interface (FFI)](/workers/languages/python/ffi), and how to use it to work with [bindings](/workers/runtime-apis/bindings) and [Runtime APIs](/workers/runtime-apis/). -- Explore the [packages](/workers/languages/python/packages) docs for instructions on how to use packages with Python Workers. diff --git a/src/content/docs/workers/languages/python/packages/fastapi.mdx b/src/content/docs/workers/languages/python/packages/fastapi.mdx index cce1c3a1d912056..0e2c8cd81fd75ee 100644 --- a/src/content/docs/workers/languages/python/packages/fastapi.mdx +++ b/src/content/docs/workers/languages/python/packages/fastapi.mdx @@ -4,10 +4,9 @@ title: FastAPI head: - tag: title content: FastAPI - --- -import { Render } from "~/components" +import { Render } from "~/components"; The FastAPI package is supported in Python Workers. @@ -17,14 +16,12 @@ The Workers runtime provides [an ASGI server](https://github.com/cloudflare/work ## Get Started - - Clone the `cloudflare/python-workers-examples` repository and run the FastAPI example: ```bash git clone https://github.com/cloudflare/python-workers-examples cd python-workers-examples/03-fastapi -npx wrangler@latest dev +uv run pywrangler dev ``` ### Example code @@ -33,11 +30,10 @@ npx wrangler@latest dev from workers import WorkerEntrypoint from fastapi import FastAPI, Request from pydantic import BaseModel +import asgi class Default(WorkerEntrypoint): async def fetch(self, request): - import asgi - return await asgi.fetch(app, request, self.env) app = FastAPI() diff --git a/src/content/docs/workers/languages/python/packages/index.mdx b/src/content/docs/workers/languages/python/packages/index.mdx index 68b4a2f448ee63a..15556e5c5e0c129 100644 --- a/src/content/docs/workers/languages/python/packages/index.mdx +++ b/src/content/docs/workers/languages/python/packages/index.mdx @@ -25,7 +25,10 @@ dependencies = [ ] [dependency-groups] -dev = ["workers-py"] +dev = [ + "workers-py", + "workers-runtime-sdk" +] ``` The above will allow your worker to depend on the [FastAPI](https://fastapi.tiangolo.com/) package. diff --git a/src/content/docs/workers/languages/python/packages/langchain.mdx b/src/content/docs/workers/languages/python/packages/langchain.mdx index 2346e3c2c53cabd..4685e3b149a3d43 100644 --- a/src/content/docs/workers/languages/python/packages/langchain.mdx +++ b/src/content/docs/workers/languages/python/packages/langchain.mdx @@ -4,29 +4,26 @@ title: Langchain head: - tag: title content: Langchain - --- -import { Render } from "~/components" +import { Render } from "~/components"; [LangChain](https://www.langchain.com/) is the most popular framework for building AI applications powered by large language models (LLMs). LangChain publishes multiple Python packages. The following are provided by the Workers runtime: -* [`langchain`](https://pypi.org/project/langchain/) (version `0.1.8`) -* [`langchain-core`](https://pypi.org/project/langchain-core/) (version `0.1.25`) -* [`langchain-openai`](https://pypi.org/project/langchain-openai/) (version `0.0.6`) +- [`langchain`](https://pypi.org/project/langchain/) (version `0.1.8`) +- [`langchain-core`](https://pypi.org/project/langchain-core/) (version `0.1.25`) +- [`langchain-openai`](https://pypi.org/project/langchain-openai/) (version `0.0.6`) ## Get Started - - Clone the `cloudflare/python-workers-examples` repository and run the LangChain example: ```bash git clone https://github.com/cloudflare/python-workers-examples cd 04-langchain -npx wrangler@latest dev +uv run pywrangler dev ``` ### Example code diff --git a/src/content/docs/workers/languages/python/stdlib.mdx b/src/content/docs/workers/languages/python/stdlib.mdx index 65cbb2a4e0ef4cf..1004638930d0d87 100644 --- a/src/content/docs/workers/languages/python/stdlib.mdx +++ b/src/content/docs/workers/languages/python/stdlib.mdx @@ -2,11 +2,10 @@ pcx_content_type: reference title: Standard Library sidebar: - order: 3 + order: 6 head: - tag: title content: Standard Library provided to Python Workers - --- Workers written in Python are executed by [Pyodide](https://pyodide.org/en/stable/index.html). @@ -17,41 +16,41 @@ The full [Python Standard Library](https://docs.python.org/3/library/index.html) ## Modules with limited functionality -* `decimal`: The decimal module has C (\_decimal) and Python (\_pydecimal) implementations +- `decimal`: The decimal module has C (\_decimal) and Python (\_pydecimal) implementations with the same functionality. Only the C implementation is available (compiled to WebAssembly) -* `pydoc`: Help messages for Python builtins are not available -* `webbrowser`: The original webbrowser module is not available. +- `pydoc`: Help messages for Python builtins are not available +- `webbrowser`: The original webbrowser module is not available. ## Excluded modules The following modules are not available in Python Workers: -* curses -* dbm -* ensurepip -* fcntl -* grp -* idlelib -* lib2to3 -* msvcrt -* pwd -* resource -* syslog -* termios -* tkinter -* turtle.py -* turtledemo -* venv -* winreg -* winsound +- curses +- dbm +- ensurepip +- fcntl +- grp +- idlelib +- lib2to3 +- msvcrt +- pwd +- resource +- syslog +- termios +- tkinter +- turtle.py +- turtledemo +- venv +- winreg +- winsound The following modules can be imported, but are not functional due to the limitations of the WebAssembly VM. -* multiprocessing -* threading -* sockets +- multiprocessing +- threading +- sockets The following are present but cannot be imported due to a dependency on the termios package which has been removed: -* pty -* tty +- pty +- tty diff --git a/src/content/docs/workers/platform/infrastructure-as-code.mdx b/src/content/docs/workers/platform/infrastructure-as-code.mdx index 1a1d495c82fc121..6fd872b3a4d9aa9 100644 --- a/src/content/docs/workers/platform/infrastructure-as-code.mdx +++ b/src/content/docs/workers/platform/infrastructure-as-code.mdx @@ -187,10 +187,11 @@ api_token="replace_me" worker_name="my-hello-world-worker" worker_script_base64=$(echo ' -from workers import Response +from workers import WorkerEntrypoint, Response -def on_fetch(request, env): - return Response(env.MESSAGE) +class Default(WorkerEntrypoint): + async def fetch(self, request): + return Response(self.env.MESSAGE) ' | base64) # Note the below will fail if the worker already exists! diff --git a/src/content/docs/workers/runtime-apis/fetch.mdx b/src/content/docs/workers/runtime-apis/fetch.mdx index c1925a7af22ac75..f8c1a5b5be0dcfc 100644 --- a/src/content/docs/workers/runtime-apis/fetch.mdx +++ b/src/content/docs/workers/runtime-apis/fetch.mdx @@ -62,17 +62,17 @@ async function eventHandler(event) { ```python -from workers import fetch, handler +from workers import WorkerEntrypoint, Response, fetch -@handler -async def on_scheduled(controller, env, ctx): - return await fetch("https://example.com", headers={"X-Source": "Cloudflare-Workers"}) +class Default(WorkerEntrypoint): + async def scheduled(self, controller, env, ctx): + return await fetch("https://example.com", headers={"X-Source": "Cloudflare-Workers"}) ``` - fetch(resource, options optional) : Promise`` - - Fetch returns a promise to a Response. +- Fetch returns a promise to a Response. ### Parameters diff --git a/src/content/docs/workers/runtime-apis/handlers/scheduled.mdx b/src/content/docs/workers/runtime-apis/handlers/scheduled.mdx index 281dbf49f191463..972a22cdf6304e4 100644 --- a/src/content/docs/workers/runtime-apis/handlers/scheduled.mdx +++ b/src/content/docs/workers/runtime-apis/handlers/scheduled.mdx @@ -57,11 +57,11 @@ export default { ```python -from workers import handler +from workers import WorkerEntrypoint, Response, fetch -@handler -async def on_scheduled(controller, env, ctx): - ctx.waitUntil(doSomeTaskOnASchedule()) +class Default(WorkerEntrypoint): + async def scheduled(self, controller, env, ctx): + ctx.waitUntil(doSomeTaskOnASchedule()) ``` @@ -69,19 +69,15 @@ async def on_scheduled(controller, env, ctx): ### Properties - `controller.cron` string - - The value of the [Cron Trigger](/workers/configuration/cron-triggers/) that started the `ScheduledEvent`. - `controller.type` string - - The type of controller. This will always return `"scheduled"`. - `controller.scheduledTime` number - - The time the `ScheduledEvent` was scheduled to be executed in milliseconds since January 1, 1970, UTC. It can be parsed as new Date(controller.scheduledTime). - `env` object - - An object containing the bindings associated with your Worker using ES modules format, such as KV namespaces and Durable Objects. - `ctx` object @@ -91,6 +87,9 @@ async def on_scheduled(controller, env, ctx): When a Workers script is invoked by a [Cron Trigger](/workers/configuration/cron-triggers/), the Workers runtime starts a `ScheduledEvent` which will be handled by the `scheduled` function in your Workers Module class. The `ctx` argument represents the context your function runs in, and contains the following methods to control what happens next: -- ctx.waitUntil(promisePromise) : void - - - Use this method to notify the runtime to wait for asynchronous tasks (for example, logging, analytics to third-party services, streaming and caching). The first `ctx.waitUntil` to fail will be observed and recorded as the status in the [Cron Trigger](/workers/configuration/cron-triggers/) Past Events table. Otherwise, it will be reported as a success. +- ctx.waitUntil(promisePromise) : void - Use this method to notify + the runtime to wait for asynchronous tasks (for example, logging, analytics to + third-party services, streaming and caching). The first `ctx.waitUntil` to + fail will be observed and recorded as the status in the [Cron + Trigger](/workers/configuration/cron-triggers/) Past Events table. Otherwise, + it will be reported as a success. diff --git a/src/content/docs/workflows/python/bindings.mdx b/src/content/docs/workflows/python/bindings.mdx index 75983d200d0b9d2..ae5201ad5f13feb 100644 --- a/src/content/docs/workflows/python/bindings.mdx +++ b/src/content/docs/workflows/python/bindings.mdx @@ -4,16 +4,16 @@ pcx_content_type: concept sidebar: order: 3 group: - hideIndex: true - + hideIndex: true --- -import { WranglerConfig } from "~/components" + +import { WranglerConfig } from "~/components"; :::caution[Python Workflows are in beta, as well as the underlying platform.] -You must add both `python_workflows` and `python_workers` compatibility flags to your `wrangler.toml` file. +You must add both `python_workflows` and `python_workers` compatibility flags to your Wrangler config file. -Also, Python Workflows requires `compatibility_date = "2025-08-01"`, or lower, to be set in your `wrangler.toml` file. +Also, Python Workflows requires `compatibility_date = "2025-08-01"`, or later, to be set in your Wrangler config file. ::: The Python Workers platform leverages FFI to access bindings to Cloudflare resources. Refer to the [bindings](/workers/languages/python/ffi/#using-bindings-from-python-workers) documentation for more information. @@ -40,7 +40,6 @@ class_name = "MyWorkflow" - And this is how you use the payload in your workflow: ```python @@ -54,23 +53,22 @@ class DemoWorkflowClass(WorkflowEntrypoint): return payload ``` - ## Workflow The `Workflow` binding gives you access to the [Workflow](/workflows/build/workers-api/#workflow) class. All its methods are available on the binding. -Under the hood, the `Workflow` binding is a Javascript object that is exposed to the Python script via [JsProxy](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy). +Under the hood, the `Workflow` binding is a Javascript object that is exposed to the Python script via [JsProxy](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy). This means that the values returned by its methods are also `JsProxy` objects, and need to be converted back into Python objects using `python_from_rpc`. - ### `create` Create (trigger) a new instance of a given Workflow. -* create(options=None) - * `options` - an **optional** dictionary of options to pass to the workflow instance. Should contain the same keys - as the [WorkflowInstanceCreateOptions](/workflows/build/workers-api/#workflowinstancecreateoptions) type. +- create(options=None)* `options` - an **optional** dictionary of + options to pass to the workflow instance. Should contain the same keys as the + [WorkflowInstanceCreateOptions](/workflows/build/workers-api/#workflowinstancecreateoptions) + type. ```python from pyodide.ffi import to_js @@ -82,6 +80,7 @@ async def on_fetch(request, env, ctx): await env.MY_WORKFLOW.create(options) return Response.json({"status": "success"}) ``` + :::note Values returned from steps need to be converted into Javascript objects using `to_js`. This is why we explicitly construct the payload using `Object.fromEntries`. @@ -117,8 +116,7 @@ await env.MY_WORKFLOW.create_batch(listOfInstances); Get a workflow instance by ID. -* get(id) - * `id` - the ID of the workflow instance to get. +- get(id)* `id` - the ID of the workflow instance to get. Returns a [`WorkflowInstance`](/workflows/build/workers-api/#workflowinstance) object, which can be used to query the status of the workflow instance. @@ -137,9 +135,8 @@ await instance.terminate() Send an event to a workflow instance. -* send_event(options) - * `type` - the type of event to send to the workflow instance. - * `payload` - the payload to send to the workflow instance. +- send_event(options)* `type` - the type of event to send to the + workflow instance. * `payload` - the payload to send to the workflow instance. ```python from pyodide.ffi import to_js diff --git a/src/content/docs/workflows/python/index.mdx b/src/content/docs/workflows/python/index.mdx index ee5964030e494a7..93af95370b805ae 100644 --- a/src/content/docs/workflows/python/index.mdx +++ b/src/content/docs/workflows/python/index.mdx @@ -7,15 +7,14 @@ sidebar: text: Beta --- - Workflow entrypoints can be declared using Python. To achieve this, you can export a `WorkflowEntrypoint` that runs on the Cloudflare Workers platform. Refer to [Python Workers](/workers/languages/python) for more information about Python on the Workers runtime. :::caution[Python Workflows are in beta, as well as the underlying platform.] -You must add both `python_workflows` and `python_workers` compatibility flags to your `wrangler.toml` file. +You must add both `python_workflows` and `python_workers` compatibility flags to your Wrangler config file. -Also, Python Workflows requires `compatibility_date = "2025-08-01"`, or lower, to be set in your `wrangler.toml` file. +Also, Python Workflows require `compatibility_date = "2025-08-01"`, or later, to be set in your Wrangler config file. Join the #python-workers channel in the [Cloudflare Developers Discord](https://discord.cloudflare.com/) and let us know what you'd like to see next. ::: @@ -42,4 +41,4 @@ To deploy a Python Workflow to Cloudflare, run [`wrangler deploy`](/workers/wran ```bash npx wrangler@latest deploy -``` \ No newline at end of file +``` diff --git a/src/content/partials/workers/python-workers-beta-packages.mdx b/src/content/partials/workers/python-workers-beta-packages.mdx deleted file mode 100644 index fe05ffcb0770925..000000000000000 --- a/src/content/partials/workers/python-workers-beta-packages.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -{} - ---- - -:::caution[Python Workers are in beta. Packages do not run in production.] - -Currently, you can only deploy Python Workers that use the standard library. [Packages](/workers/languages/python/packages/#supported-packages) **cannot be deployed** and will only work in local development for the time being. -:::