
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Fri, 03 Apr 2026 21:21:50 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Python Workers redux: fast cold starts, packages, and a uv-first workflow]]></title>
            <link>https://blog.cloudflare.com/python-workers-advancements/</link>
            <pubDate>Mon, 08 Dec 2025 06:00:00 GMT</pubDate>
            <description><![CDATA[ Recent advancements in Cloudflare Python Workers means fast cold starts, comprehensive package support, and a great developer experience. We explain how they were achieved and show how Python can be used to build serverless applications on Cloudflare. ]]></description>
            <content:encoded><![CDATA[ <p><sup><i>Note: This post was updated with additional details regarding AWS Lambda.</i></sup></p><p>Last year we announced <a href="https://blog.cloudflare.com/python-workers/"><u>basic support for Python Workers</u></a>, allowing Python developers to ship Python to region: Earth in a single command and take advantage of the <a href="https://workers.cloudflare.com/"><u>Workers platform</u></a>.</p><p>Since then, we’ve been hard at work making the <a href="https://developers.cloudflare.com/workers/languages/python/"><u>Python experience on Workers</u></a> feel great. We’ve focused on bringing package support to the platform, a reality that’s now here — with exceptionally fast cold starts and a Python-native developer experience.</p><p>This means a change in how packages are incorporated into a Python Worker. Instead of offering a limited set of built-in packages, we now support any <a href="https://pyodide.org/en/stable/usage/packages-in-pyodide.html"><u>package supported by Pyodide</u></a>, the WebAssembly runtime powering Python Workers. This includes all pure Python packages, as well as many packages that rely on dynamic libraries. We also built tooling around <a href="https://docs.astral.sh/uv/"><u>uv</u></a> to make package installation easy.</p><p>We’ve also implemented dedicated memory snapshots to reduce cold start times. These snapshots result in serious speed improvements over other serverless Python vendors. In cold start tests using common packages, Cloudflare Workers start over <b>2.4x faster than AWS Lambda</b> <b>without SnapStart </b>and <b>3x faster than Google Cloud Run</b>.</p><p>In this blog post, we’ll explain what makes Python Workers unique and share some of the technical details of how we’ve achieved the wins described above. But first, for those who may not be familiar with Workers or serverless platforms – and especially those coming from a Python background — let us share why you might want to use Workers at all.</p>
    <div>
      <h3>Deploying Python globally in 2 minutes</h3>
      <a href="#deploying-python-globally-in-2-minutes">
        
      </a>
    </div>
    <p>Part of the magic of Workers is simple code and easy global deployments. Let's start by showing how you can deploy a FastAPI app across the world with fast cold starts in less than two minutes.</p><p>A simple Worker using FastAPI can be implemented in a handful of lines:</p>
            <pre><code>from fastapi import FastAPI
from workers import WorkerEntrypoint
import asgi

app = FastAPI()

@app.get("/")
async def root():
   return {"message": "This is FastAPI on Workers"}

class Default(WorkerEntrypoint):
   async def fetch(self, request):
       return await asgi.fetch(app, request.js_object, self.env)</code></pre>
            <p>To deploy something similar, just make sure you have <code>uv</code> and <code>npm</code> installed, then run the following:</p>
            <pre><code>$ uv tool install workers-py
$ pywrangler init --template \
    https://github.com/cloudflare/python-workers-examples/03-fastapi
$ pywrangler deploy</code></pre>
            <p>With just a little code and a <code>pywrangler deploy</code>, you’ve now deployed your application across Cloudflare’s edge network that extends to <a href="https://www.cloudflare.com/network/"><u>330 locations across 125 countries</u></a>. No worrying about infrastructure or scaling.</p><p>And for many use cases, Python Workers are completely free. Our free tier offers 100,000 requests per day and 10ms CPU time per invocation. For more information, check out the <a href="https://developers.cloudflare.com/workers/platform/pricing/"><u>pricing page in our documentation</u></a>.</p><p>For more examples, <a href="https://github.com/cloudflare/python-workers-examples"><u>check out the repo in GitHub</u></a>. And read on to find out more about Python Workers.</p>
    <div>
      <h3>So what can you do with Python Workers?</h3>
      <a href="#so-what-can-you-do-with-python-workers">
        
      </a>
    </div>
    <p>Now that you’ve got a Worker, just about anything is possible. You write the code, so you get to decide. Your Python Worker receives HTTP requests and can make requests to any server on the public Internet.</p><p>You can set up cron triggers, so your Worker runs on a regular schedule. Plus, if you have more complex requirements, you can make use of <a href="https://blog.cloudflare.com/python-workflows/"><u>Workflows for Python Workers</u></a>, or even long-running WebSocket servers and clients <a href="https://developers.cloudflare.com/durable-objects/get-started/"><u>using Durable Objects</u></a>.</p><p>Here are more examples of the sorts of things you can do using Python Workers:</p><ul><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/03-fastapi"><u>Render HTML templates on the edge, with a library like Jinja, while fetching dynamic content directly from your server</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/11-opengraph"><u>Modify the response from your server — for example, you can inject opengraph tags into your HTML dynamically, based on the content requested</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/15-chatroom"><u>Build a chat room using Durable Objects and WebSockets</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/14-websocket-stream-consumer"><u>Consume data from WebSocket connections, like the Bluesky firehose</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/12-image-gen"><u>Generate images using the Pillow Python package</u></a></p></li><li><p><a href="https://github.com/cloudflare/python-workers-examples/tree/main/13-js-api-pygments"><u>Write a small Python Worker that exposes the API of a Python package, and then access it from your JavaScript Worker using RPC</u></a></p></li></ul>
    <div>
      <h3>Faster package cold starts</h3>
      <a href="#faster-package-cold-starts">
        
      </a>
    </div>
    <p>Serverless platforms like Workers save you money by only running your code when it’s necessary to do so. This means that if your Worker isn’t receiving requests, it may be shut down and will need to be restarted once a new request comes in. This typically incurs a resource overhead we refer to as the “cold start.” It’s important to keep these as short as possible to minimize latency for end users.</p><p>In standard Python, booting the runtime is expensive, and our initial implementation of Python Workers focused on making the <i>runtime</i> boot fast. However, we quickly realized that this wasn’t enough. Even if the Python runtime boots quickly, in real-world scenarios the initial startup usually includes loading modules from packages, and unfortunately, in Python many popular packages can take several seconds to load.</p><p>We set out to make cold starts fast, regardless of whether packages were loaded.</p><p>To measure realistic cold start performance, we set up a benchmark that imports common packages, as well as a benchmark running a “hello world” using a bare Python runtime. Standard Lambda is able to <a href="https://cold.picheta.me/#bare"><u>start just the runtime</u></a> quickly, but once you need to import packages, the cold start times shoot up. In order to optimize for faster cold starts with packages, you can use SnapStart on Lambda (which we will be adding to the linked benchmarks shortly). This incurs a cost to store the snapshot and an additional cost on every restore. Python Workers will automatically apply memory snapshots for free for every Python Worker.</p><p>Here are the average cold start times when loading three common packages (<a href="https://www.python-httpx.org/"><u>httpx</u></a>, <a href="https://fastapi.tiangolo.com/"><u>fastapi</u></a> and <a href="https://docs.pydantic.dev/latest/"><u>pydantic</u></a>):</p><table><tr><td><p><b>Platform</b></p></td><td><p><b>Mean Cold Start (secs)</b></p></td></tr><tr><td><p>Cloudflare Python Workers</p></td><td><p>1.027</p></td></tr><tr><td><p>AWS Lambda (without SnapStart)</p></td><td><p>2.502</p></td></tr><tr><td><p>Google Cloud Run</p></td><td><p>3.069</p></td></tr></table><p>In this case, <b>Cloudflare Python Workers have 2.4x faster cold starts than AWS Lambda without SnapStart and 3x faster cold starts than Google Cloud Run</b>. We achieved these low cold start numbers by using memory snapshots, and in a later section we explain how we did so.</p><p>We are regularly running these benchmarks. Go <a href="https://cold.picheta.me/#bare"><u>here</u></a> for up-to-date data and more info on our testing methodology.</p><p>We’re architecturally different from these other platforms — namely, <a href="https://developers.cloudflare.com/workers/reference/how-workers-works/"><u>Workers is isolate-based</u></a>. Because of that, our aims are high, and we are planning for a zero cold start future.</p>
    <div>
      <h3>Package tooling integrated with uv</h3>
      <a href="#package-tooling-integrated-with-uv">
        
      </a>
    </div>
    <p>The diverse package ecosystem is a large part of what makes Python so amazing. That’s why we’ve been hard at work ensuring that using packages in Workers is as easy as possible.</p><p>We realised that working with the existing Python tooling is the best path towards a great development experience. So we picked the <code>uv</code> package and project manager, as it’s fast, mature, and gaining momentum in the Python ecosystem.</p><p>We built our own tooling around <code>uv</code> called <a href="https://github.com/cloudflare/workers-py#pywrangler"><u>pywrangler</u></a>. This tool essentially performs the following actions:</p><ul><li><p>Reads your Worker’s pyproject.toml file to determine the dependencies specified in it</p></li><li><p>Includes your dependencies in a <code>python_modules</code> folder that lives in your Worker</p></li></ul><p>Pywrangler calls out to <code>uv</code> to install the dependencies in a way that is compatible with Python Workers, and calls out to <code>wrangler</code> when developing locally or deploying Workers. </p><p>Effectively this means that you just need to run <code>pywrangler dev</code> and <code>pywrangler</code> <code>deploy</code> to test your Worker locally and deploy it. </p>
    <div>
      <h3>Type hints</h3>
      <a href="#type-hints">
        
      </a>
    </div>
    <p>You can generate type hints for all of the <a href="https://developers.cloudflare.com/workers/runtime-apis/bindings/"><u>bindings</u></a> defined in your wrangler config using <code>pywrangler types</code>. These type hints will work with <a href="https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance"><u>Pylance</u></a> or with recent versions of <a href="https://mypy-lang.org/"><u>mypy</u></a>.</p><p>To generate the types, we use <a href="https://developers.cloudflare.com/workers/wrangler/commands/#types"><u>wrangler types</u></a> to create typescript type hints, then we use the typescript compiler to generate an abstract syntax tree for the types. Finally, we use the TypeScript hints — such as whether a JS object has an iterator field — to generate <code>mypy</code> type hints that work with the Pyodide foreign function interface.</p>
    <div>
      <h3>Decreasing cold start duration using snapshots</h3>
      <a href="#decreasing-cold-start-duration-using-snapshots">
        
      </a>
    </div>
    <p>Python startup is generally quite slow and importing a Python module can trigger a large amount of work. We avoid running Python startup during a cold start using memory snapshots.</p><p>When a Worker is deployed, we execute the Worker’s top-level scope and then take a memory snapshot and store it alongside your Worker. Whenever we are starting a new isolate for the Worker, we restore the memory snapshot and the Worker is ready to handle requests, with no need to execute any Python code in preparation. This improves cold start times considerably. For instance, starting a Worker that imports <code>fastapi</code>, <code>httpx</code> and <code>pydantic</code> without snapshots takes around 10 seconds. With snapshots, it takes 1 second.</p><p>The fact that Pyodide is built on WebAssembly enables this. We can easily capture the full linear memory of the runtime and restore it. </p>
    <div>
      <h4>Memory snapshots and Entropy</h4>
      <a href="#memory-snapshots-and-entropy">
        
      </a>
    </div>
    <p>WebAssembly runtimes do not require features like address space layout randomization for security, so most of the difficulties with memory snapshots on a modern operating system do not arise. Just like with native memory snapshots, we still have to carefully handle entropy at startup to avoid using the <a href="https://xkcd.com/221/"><u>XKCD random number generator</u></a> (we’re <a href="https://www.cloudflare.com/learning/ssl/lava-lamp-encryption/"><u>very into actual randomness</u></a>).</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Q2LYb3LZhfC2q3vQCFr1R/ff8f229715a0d37484585664c61908dc/image1.png" />
          </figure><p>By snapshotting memory, we might inadvertently lock in a seed value for randomness. In this case, future calls for “random” numbers would consistently return the same sequence of values across many requests.</p><p>Avoiding this is particularly challenging because Python uses a lot of entropy at startup. These include the libc functions <code>getentropy()</code> and <code>getrandom()</code> and also reading from <code>/dev/random</code> and <code>/dev/urandom</code>. All of these functions share the same implementation in terms of the JavaScript <code>crypto.getRandomValues()</code> function.</p><p>In Cloudflare Workers, <code>crypto.getRandomValues()</code> has always been disabled at startup in order to allow us to switch to using memory snapshots in the future. Unfortunately, the Python interpreter cannot bootstrap without calling this function. And many packages also require entropy at startup time. There are essentially two purposes for this entropy:</p><ul><li><p>Hash seeds for hash randomization</p></li><li><p>Seeds for pseudorandom number generators</p></li></ul><p>Hash randomization we do at startup time and accept the cost that each specific Worker has a fixed hash seed. Python has no mechanism to allow replacing the hash seed after startup.</p><p>For pseudorandom number generators (PRNG), we take the following approach:</p><p>At deploy time:</p><ol><li><p>Seed the PRNG with a fixed “poison seed”, then record the PRNG state.</p></li><li><p>Replace all APIs that call into the PRNG with an overlay that fails the deployment with a user error.</p></li><li><p>Execute the top level scope of user code.</p></li><li><p>Capture the snapshot.</p></li></ol><p>At run time:</p><ol><li><p>Assert that the PRNG state is unchanged. If it changed, we forgot the overlay for some method. Fail the deployment with an internal error.</p></li><li><p>After restoring the snapshot, reseed the random number generator before executing any handlers.</p></li></ol><p>With this, we can ensure that PRNGs can be used while the Worker is running, but stop Workers from using them during initialization and pre-snapshot.</p>
    <div>
      <h4>Memory snapshots and WebAssembly state</h4>
      <a href="#memory-snapshots-and-webassembly-state">
        
      </a>
    </div>
    <p>An additional difficulty arises when creating memory snapshots on WebAssembly: The memory snapshot we are saving consists only of the WebAssembly linear memory, but the full state of the Pyodide WebAssembly instance is not contained in the linear memory. </p><p>There are two tables outside of this memory.</p><p>One table holds the values of function pointers. Traditional computers use a “Von Neumann” architecture, which means that code exists in the same memory space as data, so that calling a function pointer is a jump to some memory address. WebAssembly has a “Harvard architecture” where code lives in a separate address space. This is key to most of the security guarantees of WebAssembly and in particular why WebAssembly does not need address space layout randomization. A function pointer in WebAssembly is an index into the function pointer table.</p><p>A second table holds all JavaScript objects referenced from Python. JavaScript objects cannot be directly stored into memory because the JavaScript virtual machine forbids directly obtaining a pointer to a JavaScript object. Instead, they are stored into a table and represented in WebAssembly as an index into the table.</p><p>We need to ensure that both of these tables are in exactly the same state after we restore a snapshot as they were when we captured the snapshot.</p><p>The function pointer table is always in the same state when the WebAssembly instance is initialized and is updated by the dynamic loader when we load dynamic libraries — native Python packages like numpy. </p><p>To handle dynamic loading:</p><ol><li><p>When taking the snapshot, we patch the loader to record the load order of dynamic libraries, the address in memory where the metadata for each library is allocated, and the function pointer table base address for relocations. </p></li><li><p>When restoring the snapshot, we reload the dynamic libraries in the same order, and we use a patched memory allocator to place the metadata in the same locations. We assert that the current size of the function pointer table matches the function pointer table base we recorded for the dynamic library.</p></li></ol><p>All of this ensures that each function pointer has the same meaning after we’ve restored the snapshot as it had when we took the snapshot.</p><p>To handle the JavaScript references, we implemented a fairly limited system. If a JavaScript object is accessible from globalThis by a series of property accesses, we record those property accesses and replay them when restoring the snapshot. If any reference exists to a JavaScript object that is not accessible in this way, we fail deployment of the Worker. This is good enough to deal with all the existing Python packages with Pyodide support, which do top level imports like:</p>
            <pre><code>from js import fetch</code></pre>
            
    <div>
      <h3>Reducing cold start frequency using sharding</h3>
      <a href="#reducing-cold-start-frequency-using-sharding">
        
      </a>
    </div>
    <p>Another important characteristic of our performance strategy for Python Workers is sharding. There is a very detailed description of what went into its implementation <a href="https://blog.cloudflare.com/eliminating-cold-starts-2-shard-and-conquer/"><u>here</u></a>. In short, we now route requests to existing Worker instances, whereas before we might have chosen to start a new instance.</p><p>Sharding was actually enabled for Python Workers first and proved to be a great test bed for it. A cold start is far more expensive in Python than in JavaScript, so ensuring requests are routed to an already-running isolate is especially important.</p>
    <div>
      <h3>Where do we go from here?</h3>
      <a href="#where-do-we-go-from-here">
        
      </a>
    </div>
    <p>This is just the start. We have many plans to make Python Workers better:</p><ul><li><p>More developer-friendly tooling</p></li><li><p>Even faster cold starts by utilising our isolate architecture</p></li><li><p>Support for more packages</p></li><li><p>Support for native TCP sockets, native WebSockets, and more bindings</p></li></ul><p>To learn more about Python Workers, check out the documentation available <a href="https://developers.cloudflare.com/workers/languages/python/"><u>here</u></a>. To get help, be sure to join our <a href="https://discord.cloudflare.com/"><u>Discord</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">4TlYGwUzSQPqAGP7gIEton</guid>
            <dc:creator>Dominik Picheta</dc:creator>
            <dc:creator>Hood Chatham</dc:creator>
            <dc:creator>Mike Nomitch</dc:creator>
        </item>
        <item>
            <title><![CDATA[Bringing Python to Workers using Pyodide and WebAssembly]]></title>
            <link>https://blog.cloudflare.com/python-workers/</link>
            <pubDate>Tue, 02 Apr 2024 13:00:45 GMT</pubDate>
            <description><![CDATA[ Introducing Cloudflare Workers in Python, now in open beta! We've revamped our systems to support Python, from the runtime to deployment. Learn about Python Worker's lifecycle, dynamic linking, and memory snapshots in this post ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4BCvEmK49aK7qLQuTkUsI1/0aecc2333aabe7e94ee99cdf9f830ef6/pythonweba.png" />
            
            </figure><p>Starting today, in open beta, you can now <a href="https://developers.cloudflare.com/workers/languages/python/">write Cloudflare Workers in Python</a>.</p><p>This new support for Python is different from how Workers have historically supported languages beyond JavaScript — in this case, we have directly integrated a Python implementation into <a href="https://github.com/cloudflare/workerd">workerd</a>, the open-source Workers runtime. All <a href="https://developers.cloudflare.com/workers/configuration/bindings/">bindings</a>, including bindings to <a href="https://developers.cloudflare.com/vectorize/">Vectorize</a>, <a href="https://developers.cloudflare.com/workers-ai/">Workers AI</a>, <a href="https://developers.cloudflare.com/r2/">R2</a>, <a href="https://developers.cloudflare.com/durable-objects/">Durable Objects</a>, and more are supported on day one. Python Workers can import a subset of popular Python <a href="https://developers.cloudflare.com/workers/languages/python/packages/">packages</a> including <a href="https://fastapi.tiangolo.com/">FastAPI</a>, <a href="https://python.langchain.com/docs/get_started/introduction">Langchain</a>, <a href="https://numpy.org/">Numpy</a> and more. There are no extra build steps or external toolchains.</p><p>To do this, we’ve had to push the bounds of all of our systems, from the runtime itself, to our deployment system, to the contents of the Worker bundle that is published across our <a href="https://www.cloudflare.com/network/">network</a>. You can <a href="https://developers.cloudflare.com/workers/languages/python/">read the docs</a>, and start using it today.</p><p>We want to use this post to pull back the curtain on the internal lifecycle of a Python Worker, share what we’ve learned in the process, and highlight where we’re going next.</p>
    <div>
      <h2>Beyond “Just compile to WebAssembly”</h2>
      <a href="#beyond-just-compile-to-webassembly">
        
      </a>
    </div>
    <p>Cloudflare Workers have supported WebAssembly <a href="/webassembly-on-cloudflare-workers">since 2018</a> — each Worker is a <a href="https://developers.cloudflare.com/workers/reference/how-workers-works/">V8 isolate</a>, powered by the same JavaScript engine as the Chrome web browser. In principle, it’s been <a href="/webassembly-on-cloudflare-workers">possible</a> for years to write Workers in any language — including Python — so long as it first compiles to WebAssembly or to JavaScript.</p><p>In practice, just because something is possible doesn’t mean it’s simple. And just because “hello world” works doesn’t mean you can reliably build an application. Building full applications requires supporting an ecosystem of packages that developers are used to building with. For a platform to truly support a programming language, it’s necessary to go much further than showing how to compile code using external toolchains.</p><p>Python Workers are different from what we’ve done in the past. It’s early, and still in beta, but we think it shows what providing first-class support for programming languages beyond JavaScript can look like on Workers.</p>
    <div>
      <h2>The lifecycle of a Python Worker</h2>
      <a href="#the-lifecycle-of-a-python-worker">
        
      </a>
    </div>
    <p>With Pyodide now <a href="https://github.com/cloudflare/workerd/tree/main/src/pyodide">built into workerd</a>, you can write a Worker like this:</p>
            <pre><code>from js import Response

async def on_fetch(request, env):
    return Response.new("Hello world!")</code></pre>
            <p>...with a wrangler.toml file that points to a .py file:</p>
            <pre><code>name = "hello-world-python-worker"
main = "src/entry.py"
compatibility_date = "2024-03-18"
compatibility_flags = ["python_workers"]</code></pre>
            <p>…and when you run <a href="https://developers.cloudflare.com/workers/wrangler/commands/#dev">npx wrangler@latest dev</a>, the Workers runtime will:</p><ol><li><p>Determine which <a href="https://developers.cloudflare.com/workers/languages/python/packages/">version of Pyodide</a> is required, based on your <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/">compatibility date</a></p></li><li><p>Create an isolate for your Worker, and automatically inject Pyodide</p></li><li><p>Serve your Python code using Pyodide</p></li></ol><p>This all happens under the hood — no extra toolchain or precompilation steps needed. The Python execution environment is provided for you, mirroring how Workers written in JavaScript already work.</p>
    <div>
      <h2>A Python interpreter built into the Workers runtime</h2>
      <a href="#a-python-interpreter-built-into-the-workers-runtime">
        
      </a>
    </div>
    <p>Just as JavaScript has <a href="https://en.wikipedia.org/wiki/List_of_ECMAScript_engines">many engines</a>, Python has <a href="https://wiki.python.org/moin/PythonImplementations">many implementations</a> that can execute Python code. <a href="https://github.com/python/cpython">CPython</a> is the reference implementation of Python. If you’ve used Python before, this is almost certainly what you’ve used, and is commonly referred to as just “Python”.</p><p><a href="https://pyodide.org/en/stable/">Pyodide</a> is a port of CPython to WebAssembly. It interprets Python code, without any need to precompile the Python code itself to any other format. It runs in a web browser — check out this <a href="https://pyodide-console.pages.dev/">REPL</a>. It is true to the CPython that Python developers know and expect, providing <a href="https://developers.cloudflare.com/workers/languages/python/stdlib/">most of the Python Standard Library</a>. It provides a foreign function interface (FFI) to JavaScript, allowing you to call JavaScript APIs directly from Python — more on this below. It provides popular open-source <a href="https://developers.cloudflare.com/workers/languages/python/packages/">packages</a>, and can import pure Python packages directly from PyPI.</p><p>Pyodide struck us as the perfect fit for Workers. It is designed to allow the core interpreter and each native Python module to be built as separate WebAssembly modules, dynamically linked at runtime. This allows the code footprint for these modules to be shared among all Workers running on the same machine, rather than requiring each Worker to bring its own copy. This is essential to making WebAssembly work well in the Workers environment, where we often run <a href="https://www.infoq.com/presentations/cloudflare-v8/">thousands of Workers per machine</a> — we need Workers using the same programming language to share their runtime code footprint. Running thousands of Workers on every machine is what makes it possible for us to deploy every application in every location at a <a href="/workers-pricing-scale-to-zero">reasonable price</a>.</p><p>Just like with JavaScript Workers, with Python Workers we provide the runtime for you:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4eCllqbVYDOzao6swhou7t/c4731707e15b9b37b4198f05a88682ee/VMs--Containers--ans-Isolates-comparison.png" />
            
            </figure><p>Pyodide is currently the exception — most languages that target WebAssembly do not yet support dynamic linking, so each application ends up bringing its own copy of its language runtime. We hope to see more languages support dynamic linking in the future, so that we can more effectively bring them to Workers.</p>
    <div>
      <h3>How Pyodide works</h3>
      <a href="#how-pyodide-works">
        
      </a>
    </div>
    <p>Pyodide executes Python code in WebAssembly, which is a sandboxed environment, separated from the host runtime. Unlike running native code, all operations outside of pure computation (such as file reads) must be provided by a runtime environment, then <i>imported</i> by the WebAssembly module.</p><p><a href="https://llvm.org/">LLVM</a> provides three target triples for WebAssembly:</p><ol><li><p><b>wasm32-unknown-unknown</b> – this backend provides no C standard library or system call interface; to support this backend, we would need to manually rewrite every system or library call to make use of imports we would define ourselves in the runtime.</p></li><li><p><b>wasm32-wasi</b> – WASI is a standardized system interface, and defines a standard set of imports that are implemented in WASI runtimes such as <a href="https://github.com/bytecodealliance/wasmtime/">wasmtime</a>.</p></li><li><p><b>wasm32-unknown-emscripten</b> – Like WASI, Emscripten defines the imports that a WebAssembly program needs to execute, but also outputs an accompanying JavaScript library that implements these imported functions.</p></li></ol><p>Pyodide uses Emscripten, and provides three things:</p><ol><li><p>A distribution of the CPython interpreter, compiled using Emscripten</p></li><li><p>A foreign function interface (FFI) between Python and JavaScript</p></li><li><p>A set of third-party Python packages, compiled using Emscripten’s compiler to WebAssembly.</p></li></ol><p>Of these targets, only Emscripten currently supports dynamic linking, which, as we noted above, is essential to providing a shared language runtime for Python that is shared across isolates. Emscripten does this by <a href="https://emscripten.org/docs/compiling/Dynamic-Linking.html">providing implementations of dlopen and dlsym,</a> which use the accompanying JavaScript library to modify the WebAssembly program’s table to link additional WebAssembly-compiled modules at runtime. WASI <a href="https://github.com/WebAssembly/component-model/blob/main/design/mvp/examples/SharedEverythingDynamicLinking.md#runtime-dynamic-linking">does not yet support</a> the dlopen/dlsym dynamic linking abstractions used by CPython.</p>
    <div>
      <h2>Pyodide and the magic of foreign function interfaces (FFI)</h2>
      <a href="#pyodide-and-the-magic-of-foreign-function-interfaces-ffi">
        
      </a>
    </div>
    <p>You might have noticed that in our Hello World Python Worker, we import Response from the js module:</p>
            <pre><code>from js import Response

async def on_fetch(request, env):
    return Response.new("Hello world!")</code></pre>
            <p>Why is that?</p><p>Most Workers are written in JavaScript, and most of our engineering effort on the Workers runtime goes into improving JavaScript Workers. There is a risk in adding a second language that it might never reach feature parity with the first language and always be a second class citizen. Pyodide’s foreign function interface (FFI) is critical to avoiding this by providing access to all JavaScript functionality from Python. This can be used by the Worker author directly, and it is also used to make packages like <a href="https://developers.cloudflare.com/workers/languages/python/packages/fastapi/">FastAPI</a> and <a href="https://developers.cloudflare.com/workers/languages/python/packages/langchain/">Langchain</a> work out-of-the-box, as we’ll show later in this post.</p><p>An FFI is a system for calling functions in one language that are implemented in another language. In most cases, an FFI is defined by a "higher-level" language in order to call functions implemented in a systems language, often C. Python’s <a href="https://docs.python.org/3/library/ctypes.html#module-ctypes">ctypes module</a> is such a system. These sorts of foreign function interfaces are often difficult to use because of the nature of C APIs.</p><p>Pyodide’s foreign function interface is an interface between Python and JavaScript, which are two high level object-oriented languages with a lot of design similarities. When passed from one language to another, immutable types such as strings and numbers are transparently translated. All mutable objects are wrapped in an appropriate proxy.</p><p>When a JavaScript object is passed into Python, Pyodide determines which JavaScript protocols the object supports and <a href="https://github.com/pyodide/pyodide/blob/main/src/core/jsproxy.c#L3781-L3791">dynamically constructs</a> an appropriate Python class that implements the corresponding Python protocols. For example, if the JavaScript object supports the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols">JavaScript iteration protocol</a> then the proxy will support the <a href="https://docs.python.org/3/library/stdtypes.html#iterator-types">Python iteration protocol</a>. If the JavaScript object is a Promise or other <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables">thenable</a>, the Python object will be an <a href="https://docs.python.org/3/reference/datamodel.html#awaitable-objects">awaitable</a>.</p>
            <pre><code>from js import JSON

js_array = JSON.parse("[1,2,3]")

for entry in js_array:
   print(entry)</code></pre>
            <p>The lifecycle of a request to a Python Worker makes use of Pyodide’s FFI, wrapping the incoming JavaScript <a href="https://developers.cloudflare.com/workers/runtime-apis/request/">Request</a> object in a <a href="https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.JsProxy">JsProxy</a> object that is accessible in your Python code. It then converts the value returned by the Python Worker’s <a href="https://developers.cloudflare.com/workers/runtime-apis/handlers/">handler</a> into a JavaScript <a href="https://developers.cloudflare.com/workers/runtime-apis/response/">Response</a> object that can be delivered back to the client:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/736nXjbNS1gxb4dm8xaptB/9f4dd0232d22e7d7070b1f716813a7e6/Python-Worker-Request-Lifecycle.png" />
            
            </figure>
    <div>
      <h2>Why dynamic linking is essential, and static linking isn’t enough</h2>
      <a href="#why-dynamic-linking-is-essential-and-static-linking-isnt-enough">
        
      </a>
    </div>
    <p>Python comes with <a href="https://cffi.readthedocs.io/en/stable/">a C FFI</a>, and many Python packages use this FFI to import native libraries. These libraries are typically written in C, so they must first be compiled down to WebAssembly in order to work on the Workers runtime. As we noted above, Pyodide is built with Emscripten, which overrides Python’s C FFI — any time a package tries to load a native library, it is instead loaded from a WebAssembly module that is provided by the Workers runtime. Dynamic linking is what makes this possible — it is what lets us override Python’s C FFI, allowing Pyodide to support many <a href="https://developers.cloudflare.com/workers/languages/python/packages/">Python packages</a> that have native library dependencies.</p><p>Dynamic linking is “pay as you go”, while static linking is “pay upfront” — if code is statically linked into your binary, it must be loaded upfront in order for the binary to run, even if this code is never used.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1JiF8v11hINyO6CpNnuP6a/52fad68dedc7a4d1c6beba46eb13f964/Python-Workers---Runtime.png" />
            
            </figure><p>Dynamic linking enables the Workers runtime to share the underlying WebAssembly modules of packages across different Workers that are running on the same machine.</p><p>We won’t go too much into detail on <a href="https://emscripten.org/docs/compiling/Dynamic-Linking.html#runtime-dynamic-linking-with-dlopen">how dynamic linking works in Emscripten</a>, but the main takeaway is that the Emscripten runtime fetches WebAssembly modules from a filesystem abstraction provided in JavaScript. For each Worker, we generate a filesystem at runtime, whose structure mimics a Python distribution that has the Worker’s dependencies installed, but whose underlying files are shared between Workers. This makes it possible to share Python and WebAssembly files between multiple Workers that import the same dependency. Today, we’re able to share these files across Workers, but copy them into each new isolate. We think we can go even further, by employing <a href="https://en.wikipedia.org/wiki/Copy-on-write">copy-on-write</a> techniques to share the underlying resource across many Workers.</p>
    <div>
      <h2>Supporting Server and Client libraries</h2>
      <a href="#supporting-server-and-client-libraries">
        
      </a>
    </div>
    <p>Python has a wide variety of popular HTTP client libraries, including <a href="https://www.python-httpx.org/">httpx</a>, <a href="https://pypi.org/project/urllib3/">urllib3</a>, <a href="https://pypi.org/project/requests/">requests</a> and more. Unfortunately, none of them work out of the box in Pyodide. Adding support for these has been one of the longest running user requests for the Pyodide project. The Python HTTP client libraries all work with raw sockets, and the browser security model and CORS do not allow this, so we needed another way to make them work in the Workers runtime.</p>
    <div>
      <h3>Async Client libraries</h3>
      <a href="#async-client-libraries">
        
      </a>
    </div>
    <p>For libraries that can make requests asynchronously, including <a href="https://docs.aiohttp.org/en/stable/index.html">aiohttp</a> and <a href="https://www.python-httpx.org/">httpx</a>, we can use the <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch/">Fetch API</a> to make requests. We do this by patching the library, instructing it to use the Fetch API from JavaScript — taking advantage of Pyodide’s FFI. <a href="https://github.com/cloudflare/pyodide/blob/main/packages/httpx/httpx_patch.py">The httpx patch</a> ends up quite simple —fewer than 100 lines of code. Simplified even further, it looks like this:</p>
            <pre><code>from js import Headers, Request, fetch

def py_request_to_js_request(py_request):
    js_headers = Headers.new(py_request.headers)
    return Request.new(py_request.url, method=py_request.method, headers=js_headers)

def js_response_to_py_response(js_response):
  ... # omitted

async def do_request(py_request):
  js_request = py_request_to_js_request(py_request)
    js_response = await fetch(js_request)
    py_response = js_response_to_py_response(js_response)
    return py_response</code></pre>
            
    <div>
      <h3>Synchronous Client libraries</h3>
      <a href="#synchronous-client-libraries">
        
      </a>
    </div>
    <p>Another challenge in supporting Python HTTP client libraries is that many Python APIs are synchronous. For these libraries, we cannot use the <a href="https://developers.cloudflare.com/workers/runtime-apis/fetch/">fetch API</a> directly because it is asynchronous.</p><p>Thankfully, Joe Marshall recently landed <a href="https://urllib3.readthedocs.io/en/stable/reference/contrib/emscripten.html">a contribution to urllib3</a> that adds Pyodide support in web browsers by:</p><ol><li><p>Checking if blocking with <code>Atomics.wait()</code> is possible</p><ol><li><p>If so, start a fetch worker thread</p></li><li><p>Delegate the fetch operation to the worker thread and serialize the response into a SharedArrayBuffer</p></li><li><p>In the Python thread, use Atomics.wait to block for the response in the SharedArrayBuffer</p></li></ol></li><li><p>If <code>Atomics.wait()</code> doesn’t work, fall back to a synchronous XMLHttpRequest</p></li></ol><p>Despite this, today Cloudflare Workers do not support <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">worker threads</a> or synchronous <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequest</a>, so neither of these two approaches will work in Python Workers. We do not support synchronous requests today, but there is a way forward…</p>
    <div>
      <h3>WebAssembly Stack Switching</h3>
      <a href="#webassembly-stack-switching">
        
      </a>
    </div>
    <p>There is an approach which will allow us to support synchronous requests. WebAssembly has <a href="https://github.com/WebAssembly/js-promise-integration">a stage 3 proposal adding support for stack switching</a>, which <a href="https://v8.dev/blog/jspi">v8 has an implementation of</a>. Pyodide contributors have been working on adding support for stack switching to Pyodide since September 2022, and it is almost ready.</p><p>With this support, Pyodide exposes a function called <code>run_sync</code> which can block for completion of an awaitable:</p>
            <pre><code>from pyodide.ffi import run_sync

def sync_fetch(py_request):
   js_request = py_request_to_js_request(py_request)
   js_response  = run_sync(fetch(js_request))
   return js_response_to_py_response(js_response)</code></pre>
            
    <div>
      <h3>FastAPI and Python’s Asynchronous Server Gateway Interface</h3>
      <a href="#fastapi-and-pythons-asynchronous-server-gateway-interface">
        
      </a>
    </div>
    <p><a href="https://fastapi.tiangolo.com/">FastAPI</a> is one of the most popular libraries for defining Python servers. FastAPI applications use a protocol called the <a href="https://asgi.readthedocs.io/en/latest/">Asynchronous Server Gateway Interface</a> (ASGI). This means that FastAPI never reads from or writes to a socket itself. An ASGI application expects to be hooked up to an ASGI server, typically <a href="https://www.uvicorn.org/">uvicorn</a>. The ASGI server handles all of the raw sockets on the application’s behalf.</p><p>Conveniently for us, this means that FastAPI works in Cloudflare Workers without any patches or changes to FastAPI itself. We simply need to replace <a href="https://www.uvicorn.org/">uvicorn</a> with an appropriate ASGI server that can run within a Worker. Our initial implementation lives <a href="https://github.com/cloudflare/workerd/blob/main/src/pyodide/internal/asgi.py">here</a>, in <a href="https://github.com/cloudflare/pyodide">the fork of Pyodide</a> that we maintain. We hope to add a more comprehensive feature set, add test coverage, and then upstream this implementation into Pyodide.</p><p>You can try this yourself by cloning <a href="https://github.com/cloudflare/python-workers-examples">cloudflare/python-workers-examples</a>, and running <code>npx wrangler@latest dev</code> in the directory of the FastAPI example.</p>
    <div>
      <h2>Importing Python Packages</h2>
      <a href="#importing-python-packages">
        
      </a>
    </div>
    <p>Python Workers support <a href="https://developers.cloudflare.com/workers/languages/python/packages/">a subset of Python packages</a>, which are <a href="https://github.com/cloudflare/pyodide/tree/main/packages">provided directly by Pyodide</a>, including <a href="https://numpy.org/">numpy</a>, <a href="https://www.python-httpx.org/">httpx</a>, <a href="https://developers.cloudflare.com/workers/languages/python/packages/fastapi/">FastAPI</a>, <a href="https://developers.cloudflare.com/workers/languages/python/packages/langchain/">Langchain</a>, and more. This ensures compatibility with the Pyodide runtime by pinning package versions to Pyodide versions, and allows Pyodide to patch internal implementations, as we showed above in the case of httpx.</p><p>To import a package, simply add it to your <code>requirements.txt</code> file, without adding a version number. A specific version of the package is provided directly by Pyodide. Today, you can use packages in local development, and in the coming weeks, you will be able to deploy Workers that define dependencies in a <code>requirements.txt</code> file. Later in this post, we’ll show how we’re thinking about managing new versions of Pyodide and packages.</p><p>We maintain our own fork of Pyodide, which allows us to provide patches specific to the Workers runtime, and to quickly expand our support for packages in Python Workers, while also committing to upstreaming our changes back to Pyodide, so that the whole ecosystem of developers can benefit.</p><p>Python packages are often big and memory hungry though, and they can do a lot of work at import time. How can we ensure that you can bring in the packages you need, while mitigating long cold start times?</p>
    <div>
      <h2>Making cold starts faster with memory snapshots</h2>
      <a href="#making-cold-starts-faster-with-memory-snapshots">
        
      </a>
    </div>
    <p>In the example at the start of this post, in local development, we mentioned injecting Pyodide into your Worker. Pyodide itself is 6.4 MB — and Python packages can also be quite large.</p><p>If we simply shoved Pyodide into your Worker and uploaded it to Cloudflare, that’d be quite a large Worker to load into a new isolate — cold starts would be slow. On a fast computer with a good network connection, Pyodide takes about two seconds to initialize in a web browser, one second of network time and one second of cpu time. It wouldn’t be acceptable to initialize it every time you update your code for every isolate your Worker runs in across <a href="https://www.cloudflare.com/network/">Cloudflare’s network</a>.</p><p>Instead, when you run <a href="https://developers.cloudflare.com/workers/wrangler/commands/#deploy">npx wrangler@latest deploy</a>, the following happens:</p><ol><li><p>Wrangler uploads your Python code and your <code>requirements.txt</code> file to the Workers API</p></li><li><p>We send your Python code, and your <code>requirements.txt</code> file to the Workers runtime to be validated</p></li><li><p>We create a new isolate for your Worker, and automatically inject Pyodide plus any <a href="https://developers.cloudflare.com/workers/languages/python/packages/">packages</a> you’ve specified in your <code>requirements.txt</code> file.</p></li><li><p>We scan 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.</p></li><li><p>We deploy this snapshot alongside your Worker’s Python code to Cloudflare’s network.</p></li><li><p>Just like a JavaScript Worker, we execute the Worker’s <a href="https://developers.cloudflare.com/workers/platform/limits/#worker-startup-time">top-level scope</a>.</p></li></ol><p>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:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2q0ztvdb60NUqlAsOWWFX4/203e421e31e25c5794f5fada1ad94c40/apipyth.png" />
            
            </figure><p>This takes cold starts for a basic Python Worker down to below 1 second. We’re not yet satisfied with this though. We’re confident that we can drive this down much, much further. How? By reusing memory snapshots.</p>
    <div>
      <h3>Reusing Memory Snapshots</h3>
      <a href="#reusing-memory-snapshots">
        
      </a>
    </div>
    <p>When you upload a Python Worker, we generate a single memory snapshot of the Worker’s top-level imports, including both Pyodide and any dependencies. This snapshot is specific to your Worker. It can’t be shared, even though most of its contents are the same as other Python Workers.</p><p>Instead, we can create a single, shared snapshot ahead of time, and preload it into a pool of “pre-warmed” isolates. These isolates would already have the Pyodide runtime loaded and ready — making a Python Worker work just like a JavaScript Worker. In both cases, the underlying interpreter and execution environment is provided by the Workers runtime, and available on-demand without delay. The only difference is that with Python, the interpreter runs in WebAssembly, within the Worker.</p><p>Snapshots are a common pattern across runtimes and execution environments. Node.js <a href="https://docs.google.com/document/d/1YEIBdH7ocJfm6PWISKw03szNAgnstA2B3e8PZr_-Gp4/edit#heading=h.1v0pvnoifuah">uses V8 snapshots to speed up startup time</a>. You can take <a href="https://github.com/firecracker-microvm/firecracker/blob/main/docs/snapshotting/snapshot-support.md">snapshots of Firecracker microVMs</a> and resume execution in a different process. There’s lots more we can do here — not just for Python Workers, but for Workers written in JavaScript as well, caching snapshots of compiled code from top-level scope and the state of the isolate itself. Workers are so fast and efficient that to-date we haven’t had to take snapshots in this way, but we think there are still big performance gains to be had.</p><p>This is our biggest lever towards driving cold start times down over the rest of 2024.</p>
    <div>
      <h2>Future proofing compatibility with Pyodide versions and Compatibility Dates</h2>
      <a href="#future-proofing-compatibility-with-pyodide-versions-and-compatibility-dates">
        
      </a>
    </div>
    <p>When you deploy a Worker to Cloudflare, you expect it to keep running indefinitely, even if you never update it again. There are Workers deployed in 2018 that are still running just fine in production.</p><p>We achieve this using <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/">Compatibility Dates</a> and <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/#compatibility-flags">Compatibility Flags</a>, which provide explicit opt-in mechanisms for new behavior and potentially backwards-incompatible changes, without impacting existing Workers.</p><p>This works in part because it mirrors how the Internet and web browsers work. You publish a web page with some JavaScript, and rightly expect it to work forever. Web browsers and Cloudflare Workers have the same type of commitment of stability to developers.</p><p>There is a challenge with Python though — both Pyodide and CPython are <a href="https://devguide.python.org/versions/">versioned</a>. Updated versions are published regularly and can contain breaking changes. And Pyodide provides a set of <a href="https://developers.cloudflare.com/workers/languages/python/packages/">built-in packages</a>, each with a pinned version number. This presents a question — how should we allow you to update your Worker to a newer version of Pyodide?</p><p>The answer is <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/">Compatibility Dates</a> and <a href="https://developers.cloudflare.com/workers/configuration/compatibility-dates/#compatibility-flags">Compatibility Flags</a>.</p><p>A new version of Python is released every year in August, and a new version of Pyodide is released six (6) months later. When this new version of Pyodide is published, we will add it to Workers by gating it behind a Compatibility Flag, which is only enabled after a specified Compatibility Date. This lets us continually provide updates, without risk of breaking changes, extending the commitment we’ve made for JavaScript to Python.</p><p>Each Python release has a <a href="https://devguide.python.org/versions/">five (5) year support window</a>. Once this support window has passed for a given version of Python, security patches are no longer applied, making this version unsafe to rely on. To mitigate this risk, while still trying to hold as true as possible to our commitment of stability and long-term support, after five years any Python Worker still on a Python release that is outside of the support window will be automatically moved forward to the next oldest Python release. Python is a mature and stable language, so we expect that in most cases, your Python Worker will continue running without issue. But we recommend updating the compatibility date of your Worker regularly, to stay within the support window.</p><p>In between Python releases, we also expect to update and add additional <a href="https://developers.cloudflare.com/workers/languages/python/packages/%5C">Python packages</a>, using the same opt-in mechanism. A Compatibility Flag will be a combination of the Python version and the release date of a set of packages. For example, <b>python_3.17_packages_2025_03_01</b>.</p>
    <div>
      <h2>How bindings work in Python Workers</h2>
      <a href="#how-bindings-work-in-python-workers">
        
      </a>
    </div>
    <p>We mentioned earlier that Pyodide provides a foreign function interface (FFI) to JavaScript — meaning that you can directly use JavaScript objects, methods, functions and more, directly from Python.</p><p>This means that from day one, all <a href="https://developers.cloudflare.com/workers/configuration/bindings/">binding</a> APIs to other Cloudflare resources are supported in Cloudflare Workers. The env object that is provided by handlers in Python Workers is a JavaScript object that Pyodide provides a proxy API to, handling <a href="https://pyodide.org/en/stable/usage/type-conversions.html">type translations</a> across languages automatically.</p><p>For example, to write to and read from a <a href="https://developers.cloudflare.com/kv/">KV</a> namespace from a Python Worker, you would write:</p>
            <pre><code>from js import Response

async def on_fetch(request, env):
    await env.FOO.put("bar", "baz")
    bar = await env.FOO.get("bar")
    return Response.new(bar) # returns "baz"</code></pre>
            <p>This works for Web APIs too — see how Response is imported from the js module? You can import any global from JavaScript this way.</p>
    <div>
      <h2>Get this JavaScript out of my Python!</h2>
      <a href="#get-this-javascript-out-of-my-python">
        
      </a>
    </div>
    <p>You’re probably reading this post because you want to write Python <i>instead</i> of JavaScript. <code>from js import Response</code> just isn’t Pythonic. We know — and we have actually tackled this challenge before for another language (<a href="/workers-rust-sdk">Rust</a>). And we think we can do this even better for Python.</p><p>We launched <a href="https://github.com/cloudflare/workers-rs">workers-rs</a> in 2021 to make it possible to write Workers in <a href="https://www.rust-lang.org/">Rust</a>. For each JavaScript API in Workers, we, alongside open-source contributors, have written bindings that expose a more idiomatic Rust API.</p><p>We plan to do the same for Python Workers — starting with the bindings to <a href="https://developers.cloudflare.com/workers-ai/">Workers AI</a> and <a href="https://developers.cloudflare.com/vectorize/">Vectorize</a>. But while workers-rs requires that you use and update an external dependency, the APIs we provide with Python Workers will be built into the Workers runtime directly. Just update your compatibility date, and get the latest, most Pythonic APIs.</p><p>This is about more than just making bindings to resources on Cloudflare more Pythonic though — it’s about compatibility with the ecosystem.</p><p>Similar to how we <a href="https://github.com/cloudflare/workers-rs/pull/477">recently converted</a> workers-rs to use types from the <a href="https://crates.io/crates/http">http</a> crate, which makes it easy to use the <a href="https://docs.rs/axum/latest/axum/">axum</a> crate for routing, we aim to do the same for Python Workers. For example, the Python standard library provides a <a href="https://docs.python.org/3/library/socket.html">raw socket API</a>, which many Python packages depend on. Workers already provides <a href="https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets/">connect()</a>, a JavaScript API for working with raw sockets. We see ways to provide at least a subset of the Python standard library’s socket API in Workers, enabling a broader set of Python packages to work on Workers, with less of a need for patches.</p><p>But ultimately, we hope to kick start an effort to create a standardized serverless API for Python. One that is easy to use for any Python developer and offers the same capabilities as JavaScript.</p>
    <div>
      <h2>We’re just getting started with Python Workers</h2>
      <a href="#were-just-getting-started-with-python-workers">
        
      </a>
    </div>
    <p>Providing true support for a new programming language is a big investment that goes far beyond making “hello world” work. We chose Python very intentionally — it’s the <a href="https://survey.stackoverflow.co/2023/#technology-most-popular-technologies">second most popular programming language after JavaScript</a> — and we are committed to continuing to improve performance and widen our support for Python packages.</p><p>We’re grateful to the Pyodide maintainers and the broader Python community — and we’d love to hear from you. Drop into the Python Workers channel in the <a href="https://discord.cloudflare.com/">Cloudflare Developers Discord</a>, or <a href="https://github.com/cloudflare/workerd/discussions/categories/python-packages">start a discussion on Github</a> about what you’d like to see next and which Python packages you’d like us to support.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1nmt4B6AYocqmJCw21v5pL/112dcd395906643cbf8a67de22470e13/Workers-and-Python.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[WebAssembly]]></category>
            <category><![CDATA[Python]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <category><![CDATA[WASM]]></category>
            <category><![CDATA[Developer Week]]></category>
            <guid isPermaLink="false">3Gqu0zcjgdix3M03fXEu8V</guid>
            <dc:creator>Hood Chatham</dc:creator>
            <dc:creator>Garrett Gu</dc:creator>
            <dc:creator>Dominik Picheta</dc:creator>
        </item>
        <item>
            <title><![CDATA[A Socket API that works across JavaScript runtimes — announcing a WinterCG spec and Node.js implementation of connect()]]></title>
            <link>https://blog.cloudflare.com/socket-api-works-javascript-runtimes-wintercg-polyfill-connect/</link>
            <pubDate>Thu, 28 Sep 2023 13:00:37 GMT</pubDate>
            <description><![CDATA[ Engineers from Cloudflare and Vercel have published a specification of the connect() sockets API for review by the community, along with a Node.js compatible implementation of connect() that developers can start using today ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Earlier this year, we <a href="/workers-tcp-socket-api-connect-databases/">announced a new API for creating outbound TCP sockets</a> — <a href="https://developers.cloudflare.com/workers/runtime-apis/tcp-sockets?cf_target_id=6F3FD2F2360D5526EEE56A7398DB7D9D">connect()</a>. From day one, we’ve been working with the <a href="https://wintercg.org/">Web-interoperable Runtimes Community Group (WinterCG) community</a> to chart a course toward making this API a standard, available across all runtimes and platforms — including Node.js.</p><p>Today, we’re sharing that we’ve reached a new milestone in the path to making this API available across runtimes — engineers from Cloudflare and Vercel have published <a href="https://sockets-api.proposal.wintercg.org/">a draft specification of the connect() sockets API</a> for review by the community, along with a Node.js compatible <a href="https://github.com/Ethan-Arrowood/socket">implementation of the connect() API</a> that developers can start using today.</p><p>This implementation helps both application developers and maintainers of libraries and frameworks:</p><ol><li><p>Maintainers of existing libraries that use the <a href="https://nodejs.org/api/net.html">node:net</a> and <a href="https://nodejs.org/api/tls.html">node:tls</a> APIs can use it to more easily add support for runtimes where node:net and node:tls are not available.</p></li><li><p>JavaScript frameworks can use it to make connect() available in local development, making it easier for application developers to target runtimes that provide connect().</p></li></ol>
    <div>
      <h3>Why create a new standard? Why connect()?</h3>
      <a href="#why-create-a-new-standard-why-connect">
        
      </a>
    </div>
    <p>As we <a href="/workers-tcp-socket-api-connect-databases/">described when we first announced connect()</a>, to-date there has not been a standard API across JavaScript runtimes for creating and working with TCP or UDP sockets. This makes it harder for maintainers of open-source libraries to ensure compatibility across runtimes, and ultimately creates friction for application developers who have to navigate which libraries work on which platforms.</p><p>While Node.js provides the <a href="https://nodejs.org/api/net.html">node:net</a> and <a href="https://nodejs.org/api/tls.html">node:tls</a> APIs, these APIs were designed over 10 years ago in the very early days of the Node.js project and remain callback-based. As a result, they can be hard to work with, and expose configuration in ways that don’t fit serverless platforms or web browsers.</p><p>The connect() API fills this gap by incorporating the best parts of existing socket APIs and <a href="https://github.com/WICG/direct-sockets/blob/main/docs/explainer.md">prior proposed standards</a>, based on feedback from the JavaScript community — including contributors to Node.js. Libraries like <a href="https://www.npmjs.com/package/pg">pg</a> (<a href="https://github.com/brianc/node-postgres">node-postgres</a> on Github) are already using the connect() API.</p>
    <div>
      <h3>The connect() specification</h3>
      <a href="#the-connect-specification">
        
      </a>
    </div>
    <p>At time of writing, the <a href="https://sockets-api.proposal.wintercg.org/">draft specification of the Sockets API</a> defines the following API:</p>
            <pre><code>dictionary SocketAddress {
  DOMString hostname;
  unsigned short port;
};

typedef (DOMString or SocketAddress) AnySocketAddress;

enum SecureTransportKind { "off", "on", "starttls" };

[Exposed=*]
dictionary SocketOptions {
  SecureTransportKind secureTransport = "off";
  boolean allowHalfOpen = false;
};

[Exposed=*]
interface Connect {
  Socket connect(AnySocketAddress address, optional SocketOptions opts);
};

interface Socket {
  readonly attribute ReadableStream readable;
  readonly attribute WritableStream writable;

  readonly attribute Promise&lt;undefined&gt; closed;
  Promise&lt;undefined&gt; close();

  Socket startTls();
};</code></pre>
            <p>The proposed API is Promise-based and reuses existing standards whenever possible. For example, <a href="https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream">ReadableStream</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/WritableStream">WritableStream</a> are used for the read and write ends of the socket. This makes it easy to pipe data from a TCP socket to any other library or existing code that accepts a ReadableStream as input, or to write to a TCP socket via a WritableStream.</p><p>The entrypoint of the API is the connect() function, which takes a string containing both the hostname and port separated by a colon, or an object with discrete hostname and port fields. It returns a Socket object which represents a socket connection. An instance of this object exposes attributes and methods for working with the connection.</p><p>A connection can be established in plain-text or TLS mode, as well as a special “starttls” mode which allows the socket to be easily upgraded to TLS after some period of plain-text data transfer, by calling the startTls() method on the Socket object. No need to create a new socket or switch to using a separate set of APIs once the socket is upgraded to use TLS.</p><p>For example, to upgrade a socket using the startTLS pattern, you might do something like this:</p>
            <pre><code>import { connect } from "@arrowood.dev/socket"

const options = { secureTransport: "starttls" };
const socket = connect("address:port", options);
const secureSocket = socket.startTls();
// The socket is immediately writable
// Relies on web standard WritableStream
const writer = secureSocket.writable.getWriter();
const encoder = new TextEncoder();
const encoded = encoder.encode("hello");
await writer.write(encoded);</code></pre>
            <p>Equivalent code using the node:net and node:tls APIs:</p>
            <pre><code>import net from 'node:net'
import tls from 'node:tls'

const socket = new net.Socket(HOST, PORT);
socket.once('connect', () =&gt; {
  const options = { socket };
  const secureSocket = tls.connect(options, () =&gt; {
    // The socket can only be written to once the
    // connection is established.
    // Polymorphic API, uses Node.js streams
    secureSocket.write('hello');
  }
})</code></pre>
            
    <div>
      <h3>Use the Node.js implementation of connect() in your library</h3>
      <a href="#use-the-node-js-implementation-of-connect-in-your-library">
        
      </a>
    </div>
    <p>To make it easier for open-source library maintainers to adopt the connect() API, we’ve published an <a href="https://github.com/Ethan-Arrowood/socket">implementation of connect() in Node.js</a> that allows you to publish your library such that it works across JavaScript runtimes, without having to maintain any runtime-specific code.</p><p>To get started, install it as a dependency:</p>
            <pre><code>npm install --save @arrowood.dev/socket</code></pre>
            <p>And import it in your library or application:</p>
            <pre><code>import { connect } from "@arrowood.dev/socket"</code></pre>
            
    <div>
      <h3>What’s next for connect()?</h3>
      <a href="#whats-next-for-connect">
        
      </a>
    </div>
    <p>The <a href="https://github.com/wintercg/proposal-sockets-api/">wintercg/proposal-sockets-api</a> is published as a draft, and the next step is to solicit and incorporate feedback. We’d love your feedback, particularly if you maintain an open-source library or make direct use of the node:net or node:tls APIs.</p><p>Once feedback has been incorporated, engineers from Cloudflare, Vercel and beyond will be continuing to work towards contributing an implementation of the API directly to Node.js as a built-in API.</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[TCP]]></category>
            <category><![CDATA[Node.js]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">6LC7InDwR6gLWapyPtL3u5</guid>
            <dc:creator>Dominik Picheta</dc:creator>
            <dc:creator>James M Snell</dc:creator>
            <dc:creator>Ethan Arrowood (Guest Author)</dc:creator>
        </item>
    </channel>
</rss>