
<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>Wed, 15 Apr 2026 19:41:24 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[A closer look at Python Workflows, now in beta]]></title>
            <link>https://blog.cloudflare.com/python-workflows/</link>
            <pubDate>Mon, 10 Nov 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare Workflows, our durable execution engine for running multi-step applications, now supports Python. That means less friction, more possibilities, and another reason to build on Cloudflare. ]]></description>
            <content:encoded><![CDATA[ <p>Developers can <a href="https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/"><u>already</u></a> use Cloudflare Workflows to build long-running, multi-step applications on Workers. Now, Python Workflows are here, meaning you can use your language of choice to orchestrate multi-step applications.</p><p>With <a href="https://developers.cloudflare.com/workflows/"><u>Workflows</u></a>, you can automate a sequence of idempotent steps in your application with built-in error handling and retry behavior. But Workflows were originally supported only in TypeScript. Since Python is the de facto language of choice for data pipelines, artificial intelligence/machine learning, and task automation – all of which heavily rely on orchestration – this created friction for many developers.</p><p>Over the years, we’ve been giving developers the tools to build these applications in Python, on Cloudflare. In 2020, we brought <a href="https://blog.cloudflare.com/cloudflare-workers-announces-broad-language-support/"><u>Python to Workers via Transcrypt</u></a> before directly integrating Python into <a href="https://github.com/cloudflare/workerd?cf_target_id=33101FA5C99A5BD54E7D452C9B282CD8"><u>workerd</u></a> in 2024. Earlier this year, we built support for <a href="https://developers.cloudflare.com/workers/languages/python/stdlib/"><u>CPython</u></a> along with <a href="https://pyodide.org/en/stable/usage/packages-in-pyodide.html"><u>any packages built in Pyodide</u></a>, like matplotlib and pandas, in Workers. Now, Python Workflows are supported as well, so developers can create robust applications using the language they know best.</p>
    <div>
      <h2>Why Python for Workflows?</h2>
      <a href="#why-python-for-workflows">
        
      </a>
    </div>
    <p>Imagine you’re training an <a href="https://www.cloudflare.com/learning/ai/what-is-large-language-model/"><u>LLM</u></a>. You need to label the dataset, feed data, wait for the model to run, evaluate the loss, adjust the model, and repeat. Without automation, you’d need to start each step, monitor manually until completion, and then start the next one. Instead, you could use a workflow to orchestrate the training of the model, triggering each step pending the completion of its predecessor. For any manual adjustments needed, like evaluating the loss and adjusting the model accordingly, you can implement a step that notifies you and waits for the necessary input.</p><p>Consider data pipelines, which are a top Python use case for ingesting and processing data. By automating the data pipeline through a defined set of idempotent steps, developers can deploy a workflow that handles the entire data pipeline for them.</p><p>Take another example: building <a href="https://www.cloudflare.com/learning/ai/what-is-agentic-ai/"><u>AI agents</u></a>, such as an agent to manage your groceries. Each week, you input your list of recipes, and the agent (1) compiles the list of necessary ingredients, (2) checks what ingredients you have left over from previous weeks, and (3) orders the differential for pickup from your local grocery store. Using a Workflow, this could look like:</p><ol><li><p><code>await step.wait_for_event()</code> the user inputs the grocery list</p></li><li><p><code>step.do()</code> compile list of necessary ingredients</p></li><li><p><code>step.do()</code> check list of necessary ingredients against left over ingredients</p></li><li><p><code>step.do()</code> make an API call to place the order</p></li><li><p><code>step.do() </code>proceed with payment</p></li></ol><p>Using workflows as a tool to <a href="https://agents.cloudflare.com/"><u>build agents on Cloudflare</u></a> can simplify agents’ architecture and improve their odds for reaching completion through individual step retries and state persistence. Support for Python Workflows means building agents with Python is easier than ever.</p>
    <div>
      <h3>How Python Workflows work</h3>
      <a href="#how-python-workflows-work">
        
      </a>
    </div>
    <p>Cloudflare Workflows uses the underlying infrastructure that we created for durable execution, while providing an idiomatic way for Python users to write their workflows. In addition, we aimed for complete feature parity between the Javascript and the Python SDK. This is possible because Cloudflare Workers support Python directly in the runtime itself. </p>
    <div>
      <h4>Creating a Python Workflow</h4>
      <a href="#creating-a-python-workflow">
        
      </a>
    </div>
    <p>Cloudflare Workflows are fully built on top of <a href="https://www.cloudflare.com/developer-platform/products/workers/"><u>Workers</u></a> and <a href="https://www.cloudflare.com/developer-platform/products/durable-objects/"><u>Durable Objects</u></a>. Each element plays a part in storing Workflow metadata, and instance level information. For more detail on how the Workflows platform works, <a href="https://blog.cloudflare.com/building-workflows-durable-execution-on-workers/"><u>check out this blog post</u></a>.</p><p>At the very bottom of the Workflows control plane sits the user Worker, which is the <code>WorkflowEntrypoint</code>. When the Workflow instance is ready to run, the Workflow engine will call into the <code>run</code> method of the user worker via RPC, which in this case will be a Python Worker.</p><p>This is an example skeleton for a Workflow declaration, provided by the official documentation:</p>
            <pre><code>export class MyWorkflow extends WorkflowEntrypoint&lt;Env, Params&gt; {
  async run(event: WorkflowEvent&lt;Params&gt;, step: WorkflowStep) {
    // Steps here
  }
}</code></pre>
            <p>The <code>run</code> method, as illustrated above, provides a <a href="https://developers.cloudflare.com/workflows/build/workers-api/#workflowstep"><u>WorkflowStep</u></a> parameter that implements the durable execution APIs. This is what users rely on for at-most-once execution. These APIs are implemented in JavaScript and need to be accessed in the context of the Python Worker.</p><p>A <code>WorkflowStep</code> must cross the RPC barrier, meaning the engine (caller) exposes it as an <code>RpcTarget</code>. This setup allows the user's Workflow (callee) to substitute the parameter with a stub. This stub then enables the use of durable execution APIs for Workflows by RPCing back to the engine. To read more about RPC serialization and how functions can be passed from caller and callee, read the <a href="https://developers.cloudflare.com/workers/runtime-apis/rpc/"><u>Remote-Procedure call documentation</u></a>.</p><p>All of this is true for both Python and JavaScript Workflows, since we don’t really change how the user Worker is called from the Workflows side. However, in the Python case, there is another barrier – language bridging between Python and the JavaScript module. When an RPC request targets a Python Worker, there is a Javascript entrypoint module responsible for proxying the request to be handled by the Python script, and then returned to the caller. This process typically involves type translation before and after handling the request.</p>
    <div>
      <h4>Overcoming the language barrier</h4>
      <a href="#overcoming-the-language-barrier">
        
      </a>
    </div>
    <p>Python workers rely on <a href="https://pyodide.org/en/stable/"><u>Pyodide</u></a>, which is a port of CPython to WebAssembly. Pyodide provides a foreign function interface (FFI) to JavaScript which allows for calling into JavaScript methods from Python. This is the mechanism that allows other bindings and Python packages to work within the Workers platform. Therefore, we use this FFI layer not only to allow using the Workflow binding directly, but also to provide <code>WorkflowStep</code> methods in Python. In other words, by considering that <code>WorkflowEntrypoint</code> is a special class for the runtime, the run method is manually wrapped so that <code>WorkflowStep</code> is exposed as a <a href="https://pyodide.org/en/stable/usage/api/python-api/ffi.html?cf_target_id=B32B42023AAEDEF833BCC2D9FD6096A3#pyodide.ffi.JsProxy"><u>JsProxy</u></a> instead of being type translated like other JavaScript objects. Moreover, by wrapping the APIs from the perspective of the user Worker, we allow ourselves to make some adjustments to the overall development experience, instead of simply exposing a JavaScript SDK to a different language with different semantics. </p>
    <div>
      <h4>Making the Python Workflows SDK Pythonic</h4>
      <a href="#making-the-python-workflows-sdk-pythonic">
        
      </a>
    </div>
    <p>A big part of porting Workflows to Python includes exposing an interface that Python users will be familiar with and have no problems using, similarly to what happens with our JavaScript APIs. Let's take a step back and look at a snippet for a Workflow (written in Typescript) definition.</p>
            <pre><code>import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent} from 'cloudflare:workers';
 
export class MyWorkflow extends WorkflowEntrypoint {
    async run(event: WorkflowEvent&lt;YourEventType&gt;, step: WorkflowStep) {
        let state = step.do("my first step", async () =&gt; {
          // Access your properties via event.payload
          let userEmail = event.payload.userEmail
          let createdTimestamp = event.payload.createdTimestamp
          return {"userEmail": userEmail, "createdTimestamp": createdTimestamp}
	    })
 
        step.sleep("my first sleep", "30 minutes");
 
        await step.waitForEvent&lt;EventType&gt;("receive example event", { type: "simple-event", timeout: "1 hour" })
 
   	 const developerWeek = Date.parse("22 Sept 2025 13:00:00 UTC");
        await step.sleepUntil("sleep until X times out", developerWeek)
    }
}</code></pre>
            <p>The Python implementation of the workflows API requires modification of the do method. Unlike other languages, Python does not easily support anonymous callbacks. This behavior is typically achieved through the use of <a href="https://www.w3schools.com/python/python_decorators.asp"><u>decorators</u></a>, which in this case allow us to intercept the method and expose it idiomatically. In other words, all parameters maintain their original order, with the decorated method serving as the callback.</p><p>The methods <code>waitForEvent</code>, <code>sleep</code>, and <code>sleepUntil</code> can retain their original signatures, as long as their names are converted to snake case.</p><p>Here’s the corresponding Python version for the same workflow, achieving similar behavior:</p>
            <pre><code>from workers import WorkflowEntrypoint
 
class MyWorkflow(WorkflowEntrypoint):
    async def run(self, event, step):
        @step.do("my first step")
        async def my_first_step():
            user_email = event["payload"]["userEmail"]
            created_timestamp = event["payload"]["createdTimestamp"]
            return {
                "userEmail": user_email,
                "createdTimestamp": created_timestamp,
            }
 
        await my_first_step()
 
        step.sleep("my first sleep", "30 minutes")
 
         await step.wait_for_event(
            "receive example event",
            "simple-event",
            timeout="1 hour",
        )
 
        developer_week = datetime(2024, 10, 24, 13, 0, 0, tzinfo=timezone.utc)
        await step.sleep_until("sleep until X times out", developer_week)</code></pre>
            
    <div>
      <h4>DAG Workflows</h4>
      <a href="#dag-workflows">
        
      </a>
    </div>
    <p>When designing Workflows, we’re often managing dependencies between steps even when some of these tasks can be handled concurrently. Even though we’re not thinking about it, many Workflows have a directed acyclic graph (DAG) execution flow. Concurrency is achievable in the first iteration of Python Workflows (i.e.: minimal port to Python Workers) because Pyodide captures Javascript thenables and proxies them into Python awaitables. </p><p>Consequently, <code>asyncio.gather</code> works as a counterpart to <code>Promise.all</code>. Although this is perfectly fine and ready to be used in the SDK, we also support a declarative approach.</p><p>One of the advantages of decorating the do method is that we can essentially provide further abstractions on the original API, and have them work on the entrypoint wrapper. Here’s an example of a Python API making use of the DAG capabilities introduced:</p>
            <pre><code>from workers import Response, WorkflowEntrypoint

class PythonWorkflowDAG(WorkflowEntrypoint):
    async def run(self, event, step):

        @step.do('dependency 1')
        async def dep_1():
            # does stuff
            print('executing dep1')

        @step.do('dependency 2')
        async def dep_2():
            # does stuff
            print('executing dep2')

        @step.do('demo do', depends=[dep_1, dep_2], concurrent=True)
        async def final_step(res1=None, res2=None):
            # does stuff
            print('something')

        await final_step()</code></pre>
            <p>This kind of approach makes the Workflow declaration much cleaner, leaving state management to the Workflows engine data plane, as well as the Python workers Workflow wrapper. Note that even though multiple steps can run with the same name, the engine will slightly modify the name of each step to ensure uniqueness. In Python Workflows, a dependency is considered resolved once the initial step involving it has been successfully completed.</p>
    <div>
      <h3>Try it out</h3>
      <a href="#try-it-out">
        
      </a>
    </div>
    <p>Check out <a href="https://developers.cloudflare.com/workers/languages/python/"><u>writing Workers in Python</u></a> and <a href="https://developers.cloudflare.com/workflows/python/"><u>create your first Python Workflow</u></a> today! If you have any feature requests or notice any bugs, share your feedback directly with the Cloudflare team by joining the <a href="https://discord.cloudflare.com/"><u>Cloudflare Developers community on Discord</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Workflows]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">JmiSM0vpXaKtbQJ49ehaB</guid>
            <dc:creator>Caio Nogueira</dc:creator>
            <dc:creator>Mia Malden</dc:creator>
        </item>
        <item>
            <title><![CDATA[The forecast is clear: clouds on e-paper, powered by the cloud]]></title>
            <link>https://blog.cloudflare.com/the-forecast-is-clear-clouds-on-e-paper-powered-by-the-cloud/</link>
            <pubDate>Tue, 31 Dec 2024 14:00:00 GMT</pubDate>
            <description><![CDATA[ Follow along as I build a custom weather display using Cloudflare Workers and a popular e-paper display. ]]></description>
            <content:encoded><![CDATA[ <p>I’ve noticed that many shops are increasingly using e-paper displays. They’re impressive: high contrast, no backlight, and no visible cables. Unlike most electronics, these displays are seamlessly integrated and feel very natural. This got me wondering: is it possible to use such a display for a pet project? I want to experiment with this technology myself.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Be8SEo0lqoZ0t10tPbtcT/ffa6f9263df05392d7c4408e9c98bc28/Screenshot_2024-12-23_at_15.26.23.png" />
          </figure><p><span><span><sup>(</sup></span></span><a href="https://www.wiadomoscihandlowe.pl/najwieksze-sieci-handlowe/lidl/lidl-wprowadza-do-sklepow-elektroniczne-etykiety-mniejsze-obciazenie-pracownikow-mniej-zuzytego-papieru-2447917"><span><span><sup><u>source</u></sup></span></span></a><span><span><sup>)</sup></span></span></p><p>My main goal in this project is to understand the hardware and its capabilities. Here, I'll be using an e-paper display to show the current weather, but at its core, I’m simply feeding data from a website to the display. While it sounds straightforward, it actually requires three layers of software to pull off. Still, it’s a fun challenge and a great opportunity to work with both embedded hardware and Cloudflare Workers.</p>
    <div>
      <h2>Sourcing the hardware</h2>
      <a href="#sourcing-the-hardware">
        
      </a>
    </div>
    <p>For this project, I'm using components from Waveshare. They offer <a href="https://www.waveshare.com/product/displays/e-paper/epaper-1.htm?___SID=U&amp;limit=80"><u>a variety of e-paper displays</u></a>, ranging from credit card-sized to A4-sized models. I chose the 7.5-inch, two-color "e-Paper (G)" display. For the controller, I'm using a Waveshare <a href="https://www.waveshare.com/e-Paper-ESP32-Driver-Board.htm"><u>ESP32-based universal board</u></a>. With just these two components — a display and a controller — I was ready to get started.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7A6SqnMUHriAgRAAJM5Mbj/0d1282a915a1192d276e7dff1b901013/Screenshot_2024-12-23_at_15.25.45.png" />
          </figure><p>When the components arrived, I carefully connected the display’s ribbon cable to the ESP32 board. Even though this step isn’t documented anywhere, it was simple and almost impossible to get wrong. Best of all, no soldering was needed!</p><p>That’s pretty much it for the hardware setup! I’m keeping the device powered with a 5V supply through a micro-USB connection.</p>
    <div>
      <h2>One layer of hardware </h2>
      <a href="#one-layer-of-hardware">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6WNVmUFDsLdewKrZJr4Otl/ae5eb976d1c7dc176cc288619b18c036/image1.png" />
          </figure><p><span><span><sup>(</sup></span></span><a href="https://www.waveshare.com/e-paper-esp32-driver-board.htm"><span><span><sup><u>source</u></sup></span></span></a><span><span><sup>)</sup></span></span></p><p>This was my first time working with the <a href="https://en.wikipedia.org/wiki/ESP32"><u>ESP32 CPU family</u></a>, and I’m really impressed. It’s a system-on-chip controller with built-in Bluetooth and Wi-Fi. It’s relatively fast, very power-efficient, and <a href="https://youtu.be/qLh1FOcGysY?t=157"><u>quite popular in DSP</u></a> (digital signal processing) applications. For example, your audio device might be powered by a CPU like this. Interestingly, the newer models have switched to the <a href="https://en.wikipedia.org/wiki/RISC-V"><u>RISC-V</u></a> instruction set.</p><p>For our purposes, we’ll only scratch the surface of what the ESP32 is capable of. The chip is straightforward to work with, thanks to the familiar Arduino environment. A great starting point is <a href="https://www.waveshare.com/wiki/E-Paper_ESP32_Driver_Board#How_to_Use"><u>the demo provided by Waveshare</u></a>. It sets up a web page where you can easily upload a custom image to the display.</p><p>To run the demo you need to:</p><ul><li><p>Install the <a href="https://support.arduino.cc/hc/en-us/articles/360019833020-Download-and-install-Arduino-IDE"><u>Arduino IDE</u></a>.</p></li><li><p>Fix permissions of the <code>/dev/ACM0</code> device.</p></li><li><p>Install "Additional Boards Manager URL" as per the <a href="https://www.waveshare.com/wiki/Arduino_ESP32/8266_Online_Installation"><u>instructions</u></a>, and install the "esp32 by expressif" bundle.</p></li><li><p>Open the "Loader_esp32wf" example downloaded from <a href="https://www.waveshare.com/wiki/E-Paper_ESP32_Driver_Board#Download_Demo"><u>waveshare</u></a>.</p></li><li><p>Change the Wi-Fi name, password and IP address in the Arduino IDE <code>srvr.h</code> tab.</p></li></ul><p>Once everything is set up, you should be able to connect to the ESP32’s IP address and use the simple web interface to upload an image to the display.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5KUXIJqF1J9hQqHISumuaV/85c211b315f338ba2339b4a7120162d4/Screenshot_2024-12-23_at_15.24.46.png" />
          </figure><p>With a simple click of the "Upload Image" button, the magic happens: the e-paper display comes to life, showcasing the uploaded image.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qxQCybmGmpxufDfU69OcY/d995257657f5991dd1d152b59ae2b84e/Screenshot_2024-12-23_at_15.24.09.png" />
          </figure><p>With the demo up and running, we can move on to the next step: figuring out how to render a web page on the e-paper display.</p>
    <div>
      <h2>Three layers of software</h2>
      <a href="#three-layers-of-software">
        
      </a>
    </div>
    <p>The ESP32 comes with some limitations. It has 520 KiB of RAM, 4 MiB of flash, and a 240 MHz clock speed. While this is fine for tasks like connecting to Wi-Fi or fetching a simple URL, it’s not powerful enough for more demanding tasks, such as parsing JSON or rendering an entire web page.</p><p>There are basic Arduino libraries for handling bitmaps, which can draw rectangles and render simple fonts, but manually managing layout doesn't sound appealing to me. A better approach is to play to the ESP32’s strengths — fetching and displaying bitmaps — and delegate the more complex task of HTML rendering to a more powerful server. </p><p>Let’s break the problem into three layers:</p><ol><li><p><b>ESP32 (Display Layer): </b>The ESP32 will periodically, say every minute, fetch a pre-rendered bitmap from the server and display it on the e-paper screen. This keeps the ESP32's tasks lightweight and manageable.</p></li><li><p><b>Server A (Rendering Layer): </b>This server will fetch the desired website, render it, and rasterize it into a bitmap format. Its job is to prepare a bitmap that the ESP32 can handle without additional processing.</p></li><li><p><b>Server B (Content Layer): </b>This server hosts the actual website with the HTML and CSS content. In this case, it will provide the local weather data in a styled format, ready to be fetched and rendered by Server A.</p></li></ol>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3V3LbBwfxhaWMukCVw0Tx/ab272f72ee2e45879acf3fea9a0fe75e/image8.png" />
          </figure>
    <div>
      <h3>ESP32 (Display Layer)</h3>
      <a href="#esp32-display-layer">
        
      </a>
    </div>
    <p>The ESP32 provides some great higher-level libraries to simplify development. For this project, we’ll need three key components:</p><ol><li><p><a href="https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiClientBasic/WiFiClientBasic.ino"><b><u>Wi-Fi Arduino Library</u></b></a><b>:</b> To connect the ESP32 to a Wi-Fi network.</p></li><li><p><a href="https://github.com/espressif/arduino-esp32/blob/master/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino"><b><u>HTTP Arduino Library</u></b></a><b>:</b> To handle HTTP requests and fetch the rendered bitmap from the server.</p></li><li><p><b>EPD (e-Paper Display) Driver:</b> To control the e-paper display and render the fetched bitmap.</p></li></ol><p>These libraries make it much easier to implement the required functionality without dealing with low-level details.</p><p><a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2024-12-e-paper/ESP32-fetch-from-worker/ESP32-fetch-from-worker.ino#L31"><u>Here's my ESP32 Arduino project code</u></a>. It's actually pretty straightforward:</p><ul><li><p>First, it connects to Wi-Fi</p></li><li><p>Then, it fetches a rendered bitmap from an HTTP endpoint</p></li><li><p>Then it pushes it to the e-paper display if needed</p></li><li><p>Waits a minute</p></li><li><p>And repeats the whole process forever</p></li></ul><p>E-paper displays typically start to degrade after about one million refresh cycles. To preserve the display's lifespan, I’m being extra careful to avoid unnecessary refreshes.</p>
    <div>
      <h3>Server A (Rendering Layer)</h3>
      <a href="#server-a-rendering-layer">
        
      </a>
    </div>
    <p>Now for the exciting part! We need an online service that can fetch a website, render it, rasterize it to fit our small monochromatic display, and return it as a display-sized binary blob. Initially, I considered using headless Chrome paired with an ImageMagick script, but then I discovered <a href="https://developers.cloudflare.com/browser-rendering/"><u>Cloudflare’s </u><b><u>Browser Rendering API</u></b></a>, which fits our needs perfectly.</p><p>This API can be used quite trivially and nicely fits our needs. <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2024-12-e-paper/worker-render-raster/index.ts#L79"><u>Here's the typescript worker code</u></a>, and there are two particularly interesting parts: handling a remote browser and dithering.</p>
    <div>
      <h3>Remote Browser API</h3>
      <a href="#remote-browser-api">
        
      </a>
    </div>
    <p>First, see how easy it is to render a website as a PNG using Browser Rendering:</p>
            <pre><code>if (!browser) {
       browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 600000 });
       launched = true;
}
sessionId = browser.sessionId();

const page = await browser.newPage();
await page.setViewport({
         width: 480,
         height: 800,
         deviceScaleFactor: 1,
})

await page.goto(url);
img = (await page.screenshot()) as Buffer;</code></pre>
            <p>I’m genuinely surprised at how practical and effective this approach is. While the remote browser startup isn’t exactly fast — it can take a few seconds to generate the screenshot — it’s not an issue for my use case. The delay is perfectly acceptable, especially considering how much work is offloaded to the cloud.</p>
    <div>
      <h4>Dithering</h4>
      <a href="#dithering">
        
      </a>
    </div>
    <p>To prepare the bitmap for the ESP32, we need to decode the PNG, reduce the color palette to monochromatic, and apply <a href="https://en.wikipedia.org/wiki/Dither"><u>dithering</u></a>. Here's the dithering code:</p>
            <pre><code>function ditherTwoBits(px: Buffer,
                       width: number,
                       height: number
                      ): Buffer {
    px = new Float32Array(px);

    for (let y = 0; y &lt; height; y++) {
        for (let x = 0; x &lt; width; x++) {
            const old_pixel = px[y * width + x];
            const new_pixel = old_pixel &gt; 128 ? 0xff : 0x00;

            const quant_error = (old_pixel - new_pixel) / 16.0;
            px[(y + 0) * width + (x + 0)] = new_pixel;
            px[(y + 0) * width + (x + 1)] += quant_error * 7.;
            px[(y + 1) * width + (x - 1)] += quant_error * 3.;
            px[(y + 1) * width + (x + 0)] += quant_error * 5.;
            px[(y + 1) * width + (x + 1)] += quant_error * 1.;
        }
    }

    return Buffer.from(Uint8ClampedArray.from(px));
}</code></pre>
            <p>This was my first time experimenting with dithering, and it’s been a lot of fun! I was surprised by how straightforward the process is and that it’s fully deterministic. Now that I understand the details of the algorithm, I can’t help but notice its subtle side effects everywhere — in printed materials, on screens, and even in design choices around me. It’s fascinating how something so simple has such a broad impact!</p><p>To deploy this code as a Cloudflare Worker, you only need to install the required dependencies, configure the <code>wrangler.toml</code> file, and publish the code. Here’s a step-by-step guide:</p>
            <pre><code>sudo apt install npm
cd worker-render-raster
npm install wrangler
npm install @cloudflare/puppeteer --save-dev
npm install fast-png --save-dev
npx wrangler kv:namespace create KV
npx wrangler kv:namespace create KV --preview</code></pre>
            <p>With this out of the way, you can run the code:</p>
            <pre><code>2025-01-e-paper/worker-render-raster$ npx wrangler dev --remote

 ⛅️ wrangler 3.99.0
-------------------

Your worker has access to the following bindings:
- KV Namespaces:
  - KV: XXX
- Browser:
  - Name: BROWSER
[wrangler:inf] Ready on http://localhost:46131
⎔ Starting remote preview...
Total Upload: 755.39 KiB / gzip: 149.05 KiB
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│  [b] open a browser, [d] open devtools, [l] turn on local mode, [c] clear console, [x] to exit  │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯</code></pre>
            <p>With everything set up, you can now open a browser and see a rendered and rasterized version of a website, processed through your Cloudflare Worker! For example, here’s how the <b>1.1.1.1</b> page looks in a 800x480 monochromatic resolution, complete with dithering:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6awlaJqO4LduSLwvUXQbYJ/b2b43d14c0a8d44fbed1ed4bc2a4de12/Screenshot_2024-12-23_at_15.23.14.png" />
          </figure><p>This demonstrates how effectively the Worker can handle rendering, rasterizing, and adapting web content for an e-paper display. It’s quite satisfying to see the pipeline in action.</p>
    <div>
      <h3>Server B (Content Layer)</h3>
      <a href="#server-b-content-layer">
        
      </a>
    </div>
    <p>To create the weather panel, I designed a simple HTML and CSS page and <a href="https://github.com/cloudflare/cloudflare-blog/blob/master/2024-12-e-paper/worker-weather-panel/entry.py#L165"><u>published it as another Cloudflare Worker</u></a>. This time, I used <a href="https://developers.cloudflare.com/workers/languages/python/"><u>Python in Cloudflare Workers</u></a> because it felt more straightforward, especially since the site needs to query an external weather API. The simplicity of the code was surprising and made the process smooth.</p>
            <pre><code>async def on_fetch(request, env):
    cached = await env.KV.get("weather")
    if cached:
        cached = json.loads(cached)
    else:
        u = "https://api.open-meteo.com/..."
        a = await fetch(u)
        result = await a.text()
        cached = json.loads(result)
        await env.KV.put("weather", json.dumps(cached))
    return Response.new(render(...), headers=[('content-type', 'text/html')])</code></pre>
            <p>Here’s how it appears in a normal browser compared to the rendered and rasterized version by our worker:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5irsWxy9nIgTRirXP6l8Nh/a4bb0d31df18c8385ddd96c4542941d0/Screenshot_2024-12-23_at_15.19.21.png" />
          </figure>
    <div>
      <h2>Summary</h2>
      <a href="#summary">
        
      </a>
    </div>
    <p>Finally, the display deserves a proper frame. Here’s the finished version:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Dymme1r2SJ8rAKRJjyl0K/4fa344362ca037607c7989dbf47b7fff/image11.png" />
          </figure><p>I started this project wanting to experiment with an e-paper display hardware, but I ended up spending most of my time writing software—and it turned out to be surprisingly enjoyable across all layers:</p><ul><li><p><b>ESP32:</b> The CPU is fantastic. Programming it is straightforward, thanks to powerful built-in libraries that simplify development.</p></li><li><p><b>Cloudflare Worker Browser Rendering:</b> This is an underrated but incredibly powerful technology. It made implementing features like the Floyd–Steinberg dithering algorithm surprisingly easy.</p></li><li><p><b>Cloudflare Worker Python:</b> Although still in beta, it worked flawlessly for my needs and was a great fit for handling API requests and serving dynamic content.</p></li></ul><p>It’s remarkable how much you can achieve with relatively inexpensive hardware and free Cloudflare services.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">2b03lxutI7PqAfcxswe5aE</guid>
            <dc:creator>Marek Majkowski</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[Creating serendipity with Python]]></title>
            <link>https://blog.cloudflare.com/creating-serendipity-with-python/</link>
            <pubDate>Thu, 25 Feb 2021 16:30:59 GMT</pubDate>
            <description><![CDATA[ We've been experimenting with breaking up employees into random groups (of size 4) and setting up video hangouts between them. We're doing this to replace the serendipitous meetings that sometimes occur around coffee machines, in lunch lines or while waiting for the printer. ]]></description>
            <content:encoded><![CDATA[ <p>We've been experimenting with breaking up employees into random groups (of size 4) and setting up video hangouts between them. We're doing this to replace the serendipitous meetings that sometimes occur around coffee machines, in lunch lines or while waiting for the printer. And also, we just want people to get to know each other.</p><p>Which lead to me writing some code. The core of which is <i>divide n elements into groups of at least size g minimizing the size of each group</i>. So, suppose an office has 15 employees in it then it would be divided into three groups of sizes 5, 5, 5; if an office had 16 employees it would be 4, 4, 4, 4; if it had 17 employees it would be 4, 4, 4, 5 and so on.</p><p>I initially wrote the following code (in Python):</p>
            <pre><code>    groups = [g] * (n//g)

    for e in range(0, n % g):
        groups[e % len(groups)] += 1</code></pre>
            <p>The first line creates <code>n//g</code> (<code>//</code> is integer division) entries of size <code>g</code> (for example, if <code>g == 4</code> and <code>n == 17</code> then <code>groups == [4, 4, 4, 4]</code>). The <code>for</code> loop deals with the 'left over' parts that don't divide exactly into groups of size <code>g</code>. If <code>g == 4</code> and <code>n == 17</code> then there will be one left over element to add to one of the existing <code>[4, 4, 4, 4]</code> groups resulting in <code>[5, 4, 4, 4]</code>.</p><p>The <code>e % len(groups)</code> is needed because it's possible that there are more elements left over after dividing into equal sized groups than there are entries in <code>groups</code>. For example, if <code>g == 4</code> and <code>n == 11</code> then <code>groups</code> is initially set to <code>[4, 4]</code> with three left over elements that have to be distributed into just two entries in <code>groups</code>.</p><p>So, that code works and here's the output for various sizes of <code>n</code> (and <code>g == 4</code>):</p>
            <pre><code>    4 [4]
    5 [5]
    6 [6]
    7 [7]
    8 [4, 4]
    9 [5, 4]
    10 [5, 5]
    11 [6, 5]
    12 [4, 4, 4]
    13 [5, 4, 4]
    14 [5, 5, 4]
    15 [5, 5, 5]
    16 [4, 4, 4, 4]
    17 [5, 4, 4, 4]</code></pre>
            <p>But the code irritated me because I felt there must be a simple formula to work out how many elements should be in each group. After noodling on this problem I decided to do something that's often helpful... make the problem simple and naive, or, at least, the solution simple and naive, and so I wrote this code:</p>
            <pre><code>    groups = [0] * (n//g)

    for i in range(n):
        groups[i % len(groups)] += 1</code></pre>
            <p>This is a really simple implementation. I don't like it because it loops <code>n</code> times but it helps visualize something. Imagine that <code>g == 4</code> and <code>n == 17</code>. This loop 'fills up' each entry in <code>groups</code> like this (each square is an entry in <code>groups</code> and numbers in the squares are values of <code>i</code> for which that entry was incremented by the loop).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JXiBEQfNLAjuBnKH1et8n/466e646a6b1729261839d0206eb29b18/N-elements-G-sized-groups.png" />
            
            </figure><p>So groups ends up being <code>[5, 4, 4, 4]</code>.  What this helps see is that the number of times <code>groups[i]</code> is incremented depends on the number of times the <code>for</code> loop 'loops around' on the <code>i</code>th element. And that's something that's easy to calculate without looping.</p><p>So this means that the code is now simply:</p>
            <pre><code>    groups = [1+max(0,n-(i+1))//(n//g) for i in range(n//g)]</code></pre>
            <p>And to me that is more satisfying. <code>n//g</code> is the size of <code>groups</code> which makes the loop update each entry in <code>groups</code> once. Each entry is set to <code>1 + max(0, n-(i+1))//(n//g).</code> You can think of this as follows:</p><p>1. The <code>1</code> is the first element to be dropped into each entry in <code>groups</code>.</p><p>2. <code>max(0, n-(i+1))</code> is the number of elements left over once you've placed <code>1</code> in each of the elements of <code>groups</code> up to position <code>i</code>. It's divided by <code>n//g</code> to work out how many times the process of sharing out elements (see the naive loop above) will loop around.</p><p>If #2 there isn't clear, consider the image above and imagine we are computing <code>groups[0]</code> (<code>n == 17</code> and <code>g == 4</code>). We place <code>1</code> in <code>groups[0]</code> leaving 16 elements to share out. If you naively shared them out you'd loop around four times and thus need to add 16/4 elements to <code>groups[0]</code>making it 5.</p><p>Move on to <code>groups[1]</code> and place a <code>1</code> in it. Now there are 15 elements to share out, that's 15/4 (which is 3 in integer division) and so you place 4 in <code>groups[1]</code>. And so on...</p><p>And that solution pleases me most. It succinctly creates <code>groups</code> in one shot. Of course, I might have over thought this... and others might think the other solutions are clearer or more maintainable.</p> ]]></content:encoded>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">4wAtLWwEvAD4zD9UWfijcu</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare Workers Announces Broad Language Support]]></title>
            <link>https://blog.cloudflare.com/cloudflare-workers-announces-broad-language-support/</link>
            <pubDate>Tue, 28 Jul 2020 13:01:00 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to announce support for Python, Scala, Kotlin, Reason and Dart. You can build applications on Cloudflare Workers using your favorite language starting today. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6MiZPpYQdktFLhRS4f5ZrH/244a344224171d551ce65bb5eb0848d8/Serverless-Week-Day-2_2x.png" />
            
            </figure><p>We initially launched <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> with support for JavaScript and languages that compile to WebAssembly, such as Rust, C, and C++. Since then, Cloudflare and the community have improved the usability of <a href="https://github.com/cloudflare/workers-types">Typescript on Workers</a>. But we haven't talked much about the many other <a href="https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS">popular languages that compile to JavaScript</a>. Today, we’re excited to announce support for Python, Scala, Kotlin, Reason and Dart.</p><p>You can build applications on Cloudflare Workers using your favorite language starting today.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5gnOXAk9K4vWzDnK70ndDP/983cedb85960785c9b389ef3059c12f0/Workers-Languages_2x.png" />
            
            </figure>
    <div>
      <h2><b>Getting Started</b></h2>
      <a href="#getting-started">
        
      </a>
    </div>
    <p>Getting started is as simple as installing <a href="https://github.com/cloudflare/wrangler">Wrangler</a>, then running generate for the template for your chosen language: <a href="https://github.com/cloudflare/python-worker-hello-world">Python</a>, <a href="https://github.com/cloudflare/scala-worker-hello-world">Scala</a>, <a href="https://github.com/cloudflare/kotlin-worker-hello-world">Kotlin</a>, <a href="https://github.com/cloudflare/dart-worker-hello-world">Dart</a>, or <a href="https://github.com/cloudflare/reason-worker-hello-world">Reason</a>. For Python, this looks like:</p><p><code>wrangler generate my-python-project https://github.com/cloudflare/python-worker-hello-world</code></p><p>Follow the installation instructions in the README inside the generated project directory, then run <code>wrangler publish</code>. You can see the output of your Worker at your workers.dev subdomain, e.g. <a href="https://my-python-project.cody.workers.dev/">https://my-python-project.cody.workers.dev/</a>. You can sign up for a <a href="https://dash.cloudflare.com/sign-up/workers">free Workers account</a> if you don't have one yet.</p><p>That’s it. It is really easy to write in your favorite languages. But, this wouldn’t be a very compelling blog post if we left it at that. Now, I’ll shift the focus to how we added support for these languages and how you can add support for others.</p>
    <div>
      <h3>How it all works under the hood</h3>
      <a href="#how-it-all-works-under-the-hood">
        
      </a>
    </div>
    <p>Language features are important. For instance, it's hard to give up the safety and expressiveness of <a href="https://reasonml.github.io/docs/en/pattern-matching">pattern matching</a> once you've used it. Familiar syntax matters to us as programmers.</p><p>You may also have existing code in your preferred language that you'd like to reuse. Just keep in mind that the <a href="https://www.infoq.com/presentations/cloudflare-v8/">advantages of running on V8</a> come with the limitation that if you use libraries that depend on native code or language-specific VM features, they may not translate to JavaScript. WebAssembly may be an option in that case. But for memory-managed languages you're usually better off compiling to JavaScript, at least until the story around <a href="https://github.com/WebAssembly/gc/issues/44">garbage collection for WebAssembly</a> stabilizes.</p><p>I'll walk through how the Worker language templates are made using a representative example of a dynamically typed language, Python, and a statically typed language, Scala. If you want to follow along, you'll need to have <a href="https://github.com/cloudflare/wrangler">Wrangler</a> installed and configured with your Workers account. If it's your first time using Workers it's a good idea to go through the <a href="https://developers.cloudflare.com/workers/quickstart">quickstart</a>.</p>
    <div>
      <h4>Dynamically typed languages: Python</h4>
      <a href="#dynamically-typed-languages-python">
        
      </a>
    </div>
    <p>You can generate a starter "hello world" Python project for Workers by running</p><p><code>wrangler generate my-python-project https://github.com/cloudflare/python-worker-hello-world</code></p><p>Wrangler will create a <code>my-python-project</code> directory and helpfully remind you to configure your account_id in the wrangler.toml file inside it.  The <a href="https://github.com/cloudflare/python-worker-hello-world/blob/master/README.md">README.md</a> file in the directory links to instructions on setting up <a href="http://www.transcrypt.org/docs/html/installation_use.html">Transcrypt</a>, the Python to JavaScript compiler we're using. If you already have Python 3.7 and virtualenv installed, this just requires running</p>
            <pre><code>cd my-python-project
virtualenv env
source env/bin/activate
pip install transcrypt
wrangler publish</code></pre>
            <p>The main requirement for compiling to JavaScript on Workers is the ability to produce a single js file that fits in our <a href="https://developers.cloudflare.com/workers/about/limits/#script-size">bundle size limit</a> of 1MB. Transcrypt adds about 70k for its Python runtime in this case, which is well within that limit. But by default running Transcrypt on a Python file will produce multiple JS and source map files in a <code>__target__</code> directory. Thankfully Wrangler has <a href="https://developers.cloudflare.com/workers/tooling/wrangler/webpack/">built in support for webpack</a>. There's a <a href="https://www.npmjs.com/package/transcrypt-loader">webpack loader</a> for Transcrypt, making it easy to produce a single file. See the <a href="https://github.com/cloudflare/python-worker-hello-world/blob/master/webpack.config.js">webpack.config.js</a> file for the setup.</p><p>The point of all this is to run some Python code, so let's take a look at <a href="https://github.com/cloudflare/python-worker-hello-world/blob/master/index.py">index.py</a>:</p>
            <pre><code>def handleRequest(request):
   return __new__(Response('Python Worker hello world!', {
       'headers' : { 'content-type' : 'text/plain' }
   }))

addEventListener('fetch', (lambda event: event.respondWith(handleRequest(event.request))))</code></pre>
            <p>In most respects this is very similar to any other <a href="https://developers.cloudflare.com/workers/quickstart#writing-code">Worker hello world</a>, just in Python syntax. Dictionary literals take the place of JavaScript objects, <code>lambda</code> is used instead of an anonymous arrow function, and so on. If using <code>__new__</code> to create instances of JavaScript classes seems awkward, the <a href="http://www.transcrypt.org/docs/html/special_facilities.html#creating-javascript-objects-with-new-constructor-call">Transcrypt docs</a> discuss an alternative.</p><p>Clearly, <code>addEventListener</code> is not a built-in Python function, it's part of the Workers runtime. Because Python is dynamically typed, you don't have to worry about providing type signatures for JavaScript APIs. The downside is that mistakes will result in failures when your Worker runs, rather than when Transcrypt compiles your code. Transcrypt does have experimental support for some degree of static checking using <a href="http://www.transcrypt.org/docs/html/installation_use.html#static-type-validation">mypy</a>.</p>
    <div>
      <h4>Statically typed languages: Scala</h4>
      <a href="#statically-typed-languages-scala">
        
      </a>
    </div>
    <p>You can generate a starter "hello world" Scala project for Workers by running</p><p><code>wrangler generate my-scala-project https://github.com/cloudflare/scala-worker-hello-world</code></p><p>The Scala to JavaScript compiler we're using is <a href="https://www.scala-js.org/doc/">Scala.js</a>. It has a plugin for the Scala build tool, so <a href="https://www.scala-sbt.org/1.x/docs/Setup.html">installing sbt</a> and a JDK is all you'll need.</p><p>Running <code>sbt fullOptJS</code> in the project directory will compile your Scala code to a single index.js file. The build configuration in build.sbt is set up to output to the root of the project, where Wrangler expects to find an index.js file. After that you can run <code>wrangler publish</code> as normal.</p><p>Scala.js uses the Google Closure Compiler to optimize for size when running <code>fullOptJS</code>. For the hello world, the file size is 14k. A more realistic project involving async fetch weighs in around 100k, still well within Workers limits.</p><p>In order to take advantage of static type checking, you're going to need type signatures for the JavaScript APIs you use. There are existing Scala signatures for fetch and service worker related APIs. You can see those being imported in the entry point for the Worker, <a href="https://github.com/cloudflare/scala-worker-hello-world/blob/master/src/main/scala/Main.scala">Main.scala</a>:</p>
            <pre><code>import org.scalajs.dom.experimental.serviceworkers.{FetchEvent}
import org.scalajs.dom.experimental.{Request, Response, ResponseInit}
import scala.scalajs.js</code></pre>
            <p>The import of scala.scalajs.js allows easy access to <a href="https://www.scala-js.org/doc/interoperability/types.html">Scala equivalents of JavaScript types</a>, such as <code>js.Array</code> or <code>js.Dictionary</code>. The remainder of Main looks fairly similar to a <a href="https://github.com/EverlastingBugstopper/worker-typescript-template/blob/master/src/handler.ts">Typescript Worker hello world</a>, with syntactic differences such as Unit instead of Void and square brackets instead of angle brackets for type parameters:</p>
            <pre><code>object Main {
  def main(args: Array[String]): Unit = {
    Globals.addEventListener("fetch", (event: FetchEvent) =&gt; {
      event.respondWith(handleRequest(event.request))
    })
  }

  def handleRequest(request: Request): Response = {
    new Response("Scala Worker hello world", ResponseInit(
        _headers = js.Dictionary("content-type" -&gt; "text/plain")))
  }
}  </code></pre>
            <p>Request, Response and FetchEvent are defined by the previously mentioned imports. But what's this Globals object? There are some Worker-specific extensions to JavaScript APIs. You can handle these in a statically typed language by either <a href="https://github.com/sjrd/scala-js-ts-importer">automatically converting</a> existing Typescript <a href="https://github.com/cloudflare/workers-types">type definitions for Workers</a> or by writing type signatures for the features you want to use. Writing the type signatures isn't hard, and it's good to know how to do it, so I included an example in <a href="https://github.com/cloudflare/scala-worker-hello-world/blob/master/src/main/scala/Globals.scala">Globals.scala</a>:</p>
            <pre><code>import scalajs.js
import js.annotation._

@js.native
@JSGlobalScope
object Globals extends js.Object {
  def addEventListener(`type`: String, f: js.Function): Unit = js.native
}</code></pre>
            <p>The annotation <code>@js.native</code> indicates that the implementation is in existing JavaScript code, not in Scala. That's why the body of the <code>addEventListener</code> definition is just <code>js.native</code>. In a JavaScript Worker you'd call <code>addEventListener</code> as a top-level function in global scope. Here, the <code>@JSGlobalScope</code> annotation indicates that the function signatures we're defining are available in the JavaScript global scope.</p><p>You may notice that the type of the function passed to <code>addEventListener</code> is just <code>js.Function</code>, rather than specifying the argument and return types. If you want more type safety, this could be done as <code>js.Function1[FetchEvent, Unit]</code>.  If you're trying to work quickly at the expense of safety, you could use <code>def addEventListener(any: Any*): Any</code> to allow anything.</p><p>For more information on defining types for JavaScript interfaces, see the <a href="https://www.scala-js.org/doc/interoperability/facade-types.html">Scala.js docs</a>.</p>
    <div>
      <h4>Using Workers KV and async Promises</h4>
      <a href="#using-workers-kv-and-async-promises">
        
      </a>
    </div>
    <p>Let's take a look at a more realistic example using Workers KV and asynchronous calls. The idea for the project is our own HTTP API to store and retrieve text values. For simplicity's sake I'm using the first slash-separated component of the path for the key, and the second for the value. Usage of the finished project will look like <code>PUT /meaning of life/42</code> or <code>GET /meaning of life/</code></p><p>The first thing I need is to add type signatures for the parts of the <a href="https://developers.cloudflare.com/workers/reference/apis/kv/">KV API</a> that I'm using, in Globals.scala. My KV namespace binding in wrangler.toml is just going to be named KV, resulting in a corresponding global object:</p>
            <pre><code>object Globals extends js.Object {
  def addEventListener(`type`: String, f: js.Function): Unit = js.native
  
  val KV: KVNamespace = js.native
}</code></pre>
            
            <pre><code>bash$ curl -w "\n" -X PUT 'https://scala-kv-example.cody.workers.dev/meaning of life/42'

bash$ curl -w "\n" -X GET 'https://scala-kv-example.cody.workers.dev/meaning of life/'
42</code></pre>
            <p>So what's the definition of the KVNamespace type? It's an interface, so it becomes a Scala trait with a <code>@js.native</code> annotation. The only methods I need to add right now are the simple versions of KV.get and KV.put that take and return strings. The return values are asynchronous, so they're wrapped in a <a href="https://www.scala-js.org/api/scalajs-library/1.1.0/scala/scalajs/js/Promise.html">js.Promise</a>. I'll make that wrapped string a type alias, KVValue, just in case we want to deal with the array or stream return types in the future:</p>
            <pre><code>object KVNamespace {
  type KVValue = js.Promise[String]
}

@js.native
trait KVNamespace extends js.Object {
  import KVNamespace._
  
  def get(key: String): KVValue = js.native
  
  def put(key: String, value: String): js.Promise[Unit] = js.native
}</code></pre>
            <p>With type signatures complete, I'll move on to Main.scala and how to handle interaction with JavaScript Promises. It's possible to use <code>js.Promise</code> directly, but I'd prefer to use Scala semantics for asynchronous Futures. The methods <code>toJSPromise</code> and <code>toFuture</code> from <code>js.JSConverters</code> can be used to convert back and forth:</p>
            <pre><code>  def get(key: String): Future[Response] = {
    Globals.KV.get(key).toFuture.map { (value: String) =&gt;
        new Response(value, okInit)
    } recover {
      case err =&gt;
        new Response(s"error getting a value for '$key': $err", errInit)
    }
  }</code></pre>
            <p>The function for putting values makes similar use of <code>toFuture</code> to convert the return value from KV into a Future. I use <code>map</code> to transform the value into a Response, and <code>recover</code> to handle failures. If you prefer async / await syntax instead of using combinators, you can use <a href="https://github.com/scala/scala-async">scala-async</a>.</p><p>Finally, the new definition for <code>handleRequest</code> is a good example of how pattern matching makes code more concise and less error-prone at the same time. We match on exactly the combinations of HTTP method and path components that we want, and default to an informative error for any other case:</p>
            <pre><code>  def handleRequest(request: Request): Future[Response] = {
    (request.method, request.url.split("/")) match {
      case (HttpMethod.GET, Array(_, _, _, key)) =&gt;
        get(key)
      case (HttpMethod.PUT, Array(_, _, _, key, value)) =&gt;
        put(key, value)
      case _ =&gt;
        Future.successful(
          new Response("expected GET /key or PUT /key/value", errInit))
    }
  }</code></pre>
            <p>You can get the complete code for this example by running</p>
            <pre><code>wrangler generate projectname https://github.com/cloudflare/scala-worker-kv</code></pre>
            
    <div>
      <h2>How to contribute</h2>
      <a href="#how-to-contribute">
        
      </a>
    </div>
    <p>I'm a fan of programming languages, and will continue to add more <a href="https://developers.cloudflare.com/workers/templates/">Workers templates</a>. You probably know your favorite language better than I do, so <a href="https://github.com/cloudflare/template-registry/blob/master/CONTRIBUTING.md">pull requests are welcome</a> for a simple hello world or more complex example.</p><p>And if you're into programming languages check out the <a href="https://redmonk.com/sogrady/2020/07/27/language-rankings-6-20/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=language-rankings-6-20">latest language rankings</a> from RedMonk where Python is the first non-Java or JavaScript language ever to place in the top two of these rankings.</p><p>Stay tuned for the rest of <a href="/tag/serverless-week/">Serverless Week</a>!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Serverless Week]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[Python]]></category>
            <category><![CDATA[Wrangler]]></category>
            <guid isPermaLink="false">75ucduojp4RdGzqCmeqmdX</guid>
            <dc:creator>Cody Koeninger</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Flan Scan: Cloudflare’s Lightweight Network Vulnerability Scanner]]></title>
            <link>https://blog.cloudflare.com/introducing-flan-scan/</link>
            <pubDate>Thu, 21 Nov 2019 14:00:00 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to open source Flan Scan, Cloudflare’s in-house lightweight network vulnerability scanner. Flan Scan is a thin wrapper around Nmap that converts this popular open source tool into a vulnerability scanner with the added benefit of easy deployment. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we’re excited to open source <a href="https://github.com/cloudflare/flan">Flan Scan</a>, Cloudflare’s in-house lightweight network vulnerability scanner. Flan Scan is a thin wrapper around <a href="https://nmap.org/">Nmap</a> that converts this popular open source tool into a vulnerability scanner with the added benefit of easy deployment.</p><p>We created Flan Scan after two unsuccessful attempts at using “industry standard” scanners for our compliance scans. A little over a year ago, we were paying a big vendor for their scanner until we realized it was one of our highest security costs and many of its features were not relevant to our setup. It became clear we were not getting our money’s worth. Soon after, we switched to an open source scanner and took on the task of managing its complicated setup. That made it difficult to deploy to our entire fleet of more than 190 data centers.</p><p>We had a deadline at the end of Q3 to complete an internal scan for our compliance requirements but no tool that met our needs. Given our history with existing scanners, we decided to set off on our own and build a scanner that worked for our setup. To design Flan Scan, we worked closely with our auditors to understand the requirements of such a tool. We needed a scanner that could accurately detect the services on our network and then lookup those services in a database of <a href="https://cve.mitre.org/">CVEs</a> to find vulnerabilities relevant to our services. Additionally, unlike other scanners we had tried, our tool had to be easy to deploy across our entire network.</p><p>We chose Nmap as our base scanner because, unlike other network scanners which sacrifice accuracy for speed, it prioritizes detecting services thereby reducing false positives. We also liked Nmap because of the Nmap Scripting Engine (NSE), which allows scripts to be run against the scan results. We found that the <a href="https://github.com/vulnersCom/nmap-vulners">“vulners” script</a>, available on NSE, mapped the detected services to relevant CVEs from a database, which is exactly what we needed.</p><p>The next step was to make the scanner easy to deploy while ensuring it outputted actionable and valuable results. We added three features to Flan Scan which helped package up Nmap into a user-friendly scanner that can be deployed across a large network.</p><ul><li><p><b>Easy Deployment and Configuration </b><b><b>-</b></b> To create a lightweight scanner with easy configuration, we chose to run Flan Scan inside a Docker container. As a result, Flan Scan can be built and pushed to a Docker registry and maintains the flexibility to be configured at runtime. Flan Scan also includes sample Kubernetes configuration and deployment files with a few placeholders so you can get up and scanning quickly.</p></li><li><p><b>Pushing results to the Cloud </b><b><b>-</b></b> Flan Scan adds support for pushing results to a Google Cloud Storage Bucket or an S3 bucket. All you need to do is set a few environment variables and Flan Scan will do the rest. This makes it possible to run many scans across a large network and collect the results in one central location for processing.</p></li><li><p><b>Actionable Reports</b> - Flan Scan generates actionable reports from Nmap’s output so you can quickly identify vulnerable services on your network, the applicable CVEs, and the IP addresses and ports where these services were found. The reports are useful for engineers following up on the results of the scan as well as auditors looking for evidence of compliance scans.</p></li></ul>
            <figure>
            
            <img src="https://downloads.ctfassets.net/zkvhlag99gkb/2P4YSEYrC1ofUi7vKitDR4/284e76df1c2b00c24e40bc495962595d/New_report_gif.gif" />
            
            </figure><p>Sample run of Flan Scan from start to finish. </p>
    <div>
      <h2>How has Flan Scan improved Cloudflare's network security?</h2>
      <a href="#how-has-flan-scan-improved-cloudflares-network-security">
        
      </a>
    </div>
    <p>By the end of Q3, not only had we completed our compliance scans, we also used Flan Scan to tangibly improve the security of our network. At Cloudflare, we pin the software version of some services in production because it allows us to prioritize upgrades by weighing the operational cost of upgrading against the improvements of the latest version. Flan Scan’s results revealed that our FreeIPA nodes, used to manage Linux users and hosts, were running an outdated version of Apache with several medium severity vulnerabilities. As a result, we prioritized their update. Flan Scan also found a vulnerable instance of PostgreSQL leftover from a performance dashboard that no longer exists.</p><p>Flan Scan is part of a larger effort to expand our vulnerability management program. We recently deployed <a href="https://osquery.io/">osquery</a> to our entire network to perform host-based vulnerability tracking. By complementing osquery’s findings with Flan Scan’s network scans we are working towards comprehensive visibility of the services running at our edge and their vulnerabilities. With two vulnerability trackers in place, we decided to build a tool to manage the increasing number of vulnerability  sources. Our tool sends alerts on new vulnerabilities, filters out false positives, and tracks remediated vulnerabilities. Flan Scan’s valuable security insights were a major impetus for creating this vulnerability tracking tool.</p>
    <div>
      <h2>How does Flan Scan work?</h2>
      <a href="#how-does-flan-scan-work">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6kAAc3ohubexEKfI60A0nS/9101e8229e320c6b53771ad09ab87e0e/FLan-scan-diagram_3x.png" />
            
            </figure><p>The first step of Flan Scan is running an Nmap scan with service detection. Flan Scan's default Nmap scan runs the following scans:</p><ol><li><p><b>ICMP ping scan -</b> Nmap determines which of the IP addresses given are online.</p></li><li><p><b>SYN scan -</b> Nmap scans the 1000 most common ports of the IP addresses which responded to the ICMP ping. Nmap marks ports as open, closed, or filtered.</p></li><li><p><b>Service detection scan -</b> To detect which services are running on open ports Nmap performs TCP handshake and <a href="https://en.wikipedia.org/wiki/Banner_grabbing">banner grabbing</a> scans.</p></li></ol><p>Other types of scanning such as UDP scanning and IPv6 addresses are also possible with Nmap. Flan Scan allows users to run these and any other extended features of Nmap by passing in Nmap flags at runtime.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6jGqNtdhSePb0Iz3JEUCZF/c04a4b36066b7e083be64c5306df5eb4/Screen-Shot-2019-11-11-at-2.15.01-PM.png" />
            
            </figure><p>Sample Nmap output</p><p>Flan Scan adds the "vulners" script tag in its default Nmap command to include in the output a list of vulnerabilities applicable to the services detected. The vulners script works by making API calls to a service run by <a href="https://vulners.com/">vulners.com</a> which returns any known vulnerabilities for the given service.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/476YY2nBlzz4hMVJVfQ98h/f5bf27b240590dfb5ce700258875aa5b/Screen-Shot-2019-11-11-at-2.28.58-PM.png" />
            
            </figure><p>Sample Nmap output with Vulners script</p><p>The next step of Flan Scan uses a Python script to convert the structured XML of Nmap’s output to an actionable report. The reports of the previous scanner we used listed each of the IP addresses scanned and present the vulnerabilities applicable to that location. Since we had multiple IP addresses running the same service, the report would repeat the same list of vulnerabilities under each of these IP addresses. This meant scrolling back and forth on documents hundreds of pages long to obtain a list of all IP addresses with the same vulnerabilities.  The results were impossible to digest.</p><p>Flan Scans results are structured around services. The report enumerates all vulnerable services with a list beneath each one of relevant vulnerabilities and all IP addresses running this service. This structure makes the report shorter and actionable since the services that need to be remediated can be clearly identified. Flan Scan reports are made using <a href="https://www.latex-project.org/">LaTeX</a> because who doesn’t like nicely formatted reports that can be generated with a script? The raw LaTeX file that Flan Scan outputs can be converted to a beautiful PDF by using tools like pdf2latex or TeXShop.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/VR5TpcOccduNyZWRNYN8A/b13b1da0c38657eb8244cd396061799a/Screen-Shot-2019-11-18-at-4.51.06-PM.png" />
            
            </figure><p>Sample Flan Scan report</p>
    <div>
      <h2>What’s next?</h2>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Cloudflare’s mission is to help build a better Internet for everyone, not just Internet giants who can afford to buy expensive tools. We’re open sourcing Flan Scan because we believe it shouldn’t cost tons of money to have <a href="https://www.cloudflare.com/network-security/">strong network security</a>.</p><p>You can get started running a vulnerability scan on your network in a few minutes by following the instructions on the <a href="https://github.com/cloudflare/flan">README</a>. We welcome contributions and suggestions from the community.</p> ]]></content:encoded>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Python]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">7LprqimW4zLRcwCAGWykIi</guid>
            <dc:creator>Nadin El-Yabroudi</dc:creator>
        </item>
        <item>
            <title><![CDATA[A New API Binding: cloudflare-php]]></title>
            <link>https://blog.cloudflare.com/cloudflare-php-api-binding/</link>
            <pubDate>Sat, 23 Sep 2017 00:01:35 GMT</pubDate>
            <description><![CDATA[ Back in May last year, one of my colleagues blogged about the introduction of our Python binding for the Cloudflare API and drew reference to our other bindings in Go and Node. Today we are complimenting this range by introducing a new official binding, this time in PHP. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Back in May last year, one of my colleagues blogged about the introduction of our <a href="/python-cloudflare/">Python binding for the Cloudflare API</a> and drew reference to our other bindings in <a href="https://github.com/cloudflare/cloudflare-go">Go</a> and <a href="https://github.com/cloudflare/node-cloudflare">Node</a>. Today we are complimenting this range by introducing a new official binding, this time in <a href="https://github.com/cloudflare/cloudflare-php">PHP</a>.</p><p>This binding is available via Packagist as <a href="https://packagist.org/packages/cloudflare/sdk">cloudflare/sdk</a>, you can install it using Composer simply by running <code>composer require cloudflare/sdk</code>. We have documented various use-cases in our <a href="https://support.cloudflare.com/hc/en-us/articles/115001661191">"Cloudflare PHP API Binding" KB article</a> to help you get started.</p><p>Alternatively should you wish to help contribute, or just give us a star on GitHub, feel free to browse to the <a href="https://github.com/cloudflare/cloudflare-php">cloudflare-php source code</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1cb8c0aCRK1QxtwReVtMDc/de48b0c9191df68bab840c53f5efea8b/installing-cloudflare-php.png" />
            
            </figure><p>PHP is a controversial language, and there is no doubt there are elements of bad design within the language (as is the case with many other languages). However, love it or hate it, PHP is a language of high adoption; as of September 2017 <a href="https://w3techs.com/technologies/overview/programming_language/all">W3Techs</a> report that PHP is used by 82.8% of all the websites whose server-side programming language is known. In creating this binding the question clearly wasn't on the merits of PHP, but whether we wanted to help drive improvements to the developer experience for the sizeable number of developers integrating with us whilst using PHP.</p><p>In order to help those looking to contribute or build upon this library, I write this blog post to explain some of the design decisions made in putting this together.</p>
    <div>
      <h3>Exclusively for PHP 7</h3>
      <a href="#exclusively-for-php-7">
        
      </a>
    </div>
    <p>PHP 5 initially introduced the ability for type hinting on the basis of classes and interfaces, this opened up (albeit seldom used) parametric polymorphic behaviour in PHP. Type hinting on the basis of interfaces made it easier for those developing in PHP to follow the Gang of Four's famous guidance: "Program to an 'interface', not an 'implementation'."</p><p>Type hinting has slowly developed in PHP, in PHP 7.0 the ability for Scalar Type Hinting was released after a few rounds of RFCs. Additionally PHP 7.0 introduced Return Type Declarations, allowing return values to be type hinted in a similar way to argument type hinting. In this library we extensively use Scalar Type Hinting and Return Type Declarations thereby restricting the backward compatibility that's available with PHP 5.</p><p>In order for backward compatibility to be available, these improvements to type hinting simply would not be implementable and the associated benefits would be lost. With Active Support <a href="http://php.net/supported-versions.php">no longer being offered to PHP 5.6</a> and Security Support little over a year away from disappearing for the entirety of PHP 5.x, we decided the additional coverage wasn't worth the cost.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/RJX9jaiMJuc3J7e8OBCor/2998f5d9da54264b9774aa8e18fe00b6/php-support.png" />
            
            </figure>
    <div>
      <h3>Object Composition</h3>
      <a href="#object-composition">
        
      </a>
    </div>
    <blockquote><p>What do we mean by a software architecture? To me the term architecture conveys a notion of the core elements of the system, the pieces that are difficult to change. A foundation on which the rest must be built. <a href="https://www.martinfowler.com/articles/designDead.html">Martin Fowler</a></p></blockquote><p>When getting started with this package, you'll notice there are 3 classes you'll need to instantiate:</p>
            <pre><code>$key     = new \Cloudflare\API\Auth\APIKey('user@example.com', 'apiKey');
$adapter = new Cloudflare\API\Adapter\Guzzle($key);
$user    = new \Cloudflare\API\Endpoints\User($adapter);
    
echo $user-&gt;getUserID();</code></pre>
            <p>The first class being instantiated is called <code>APIKey</code> (a few other classes for authentication are available). We then proceed to instantiate the <code>Guzzle</code> class and the <code>APIKey</code> object is then injected into the constructor of the <code>Guzzle</code> class. The <code>Auth</code> interface that the <code>APIKey</code> class implements is fairly simple:</p>
            <pre><code>namespace Cloudflare\API\Auth;

interface Auth
{
    public function getHeaders(): array;
}</code></pre>
            <p>The <code>Adapter</code> interface (which the <code>Guzzle</code> class implements) makes explicit that an object built on the <code>Auth</code> interface is expected to be injected into the constructor:</p>
            <pre><code>namespace Cloudflare\API\Adapter;

use Cloudflare\API\Auth\Auth;
use Psr\Http\Message\ResponseInterface;

interface Adapter
{
...
    public function __construct(Auth $auth, string $baseURI);
...
}</code></pre>
            <p>In doing so; we define that classes which implement the <code>Adapter</code> interface are to be composed using objects made from classes which implement the <code>Auth</code> interface.</p><p>So why am I explaining basic Dependency Injection here? It is critical to understand as the design of our API changes, the mechanisms for Authentication may vary independently of the HTTP Client or indeed API Endpoints themselves. Similarly the HTTP Client or the API Endpoints may vary independently of the other elements involved. Indeed, this package already contains three classes for the purpose of authentication (<code>APIKey</code>, <code>UserServiceKey</code> and <code>None</code>) which need to be interchangeably used. This package therefore considers the possibility for changes to different components in the API and seeks to allow these components to vary independently.</p><p>Dependency Injection is also used where the parameters for an API Endpoint become more complicated then what is permitted by simpler variables types; for example, this is done for defining the Target or Configuration when configuring a Page Rule:</p>
            <pre><code>require_once('vendor/autoload.php');

$key = new \Cloudflare\API\Auth\APIKey('mjsa@junade.com', 'apiKey');
$adapter = new Cloudflare\API\Adapter\Guzzle($key);
$zones = new \Cloudflare\API\Endpoints\Zones($adapter);

$zoneID = $zones-&gt;getZoneID("junade.com");

$pageRulesTarget = new \Cloudflare\API\Configurations\PageRulesTargets('https://junade.com/noCache/*');

$pageRulesConfig = new \Cloudflare\API\Configurations\PageRulesActions();
$pageRulesConfig-&gt;setCacheLevel('bypass');

$pageRules = new \Cloudflare\API\Endpoints\PageRules($adapter);
$pageRules-&gt;createPageRule($zoneID, $pageRulesTarget, $pageRulesConfig, true, 6);</code></pre>
            <p>The structure of this project is overall based on simple object composition; this provides a far more simple object model for the long-term and a design that provides higher flexibility. For example; should we later want to create an Endpoint class which is a <a href="https://en.wikipedia.org/wiki/Composite_pattern">composite</a> of other Endpoints, it becomes fairly trivial for us to build this by implementing the same interface as the other Endpoint classes. As more code is added, we are able to keep the design of the software relatively thinly layered.</p>
    <div>
      <h3>Testing/Mocking HTTP Requests</h3>
      <a href="#testing-mocking-http-requests">
        
      </a>
    </div>
    <p>If you're interesting in helping contribute to this repository; there are two key ways you can help:</p><ol><li><p>Building out coverage of endpoints on our API</p></li><li><p>Building out test coverage of those endpoint classes</p></li></ol><p>The PHP-FIG (PHP Framework Interop Group) put together a standard on how HTTP responses can be represented in an interface, this is described in the <a href="http://www.php-fig.org/psr/psr-7/">PSR-7 standard</a>. This response interface is utilised by our HTTP <code>Adapter</code> interface in which responses to API requests are type hinted to this interface (<code>Psr\Http\Message\ResponseInterface</code>).</p><p>By using this standard, it's easier to add further abstractions for additional HTTP clients and mock HTTP responses for unit testing. Let's assume the JSON response is stored in the <code>$response</code> variable and we want to test the <code>listIPs</code> method in the <code>IPs</code> Endpoint class:</p>
            <pre><code>public function testListIPs() {
  $stream = GuzzleHttp\Psr7\stream_for($response);
  $response = new GuzzleHttp\Psr7\Response(200, ['Content-Type' =&gt; 'application/json'], $stream);
  $mock = $this-&gt;getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)-&gt;getMock();
  $mock-&gt;method('get')-&gt;willReturn($response);

  $mock-&gt;expects($this-&gt;once())
    -&gt;method('get')
    -&gt;with($this-&gt;equalTo('ips'), $this-&gt;equalTo([])
  );

   $ips = new \Cloudflare\API\Endpoints\IPs($mock);
   $ips = $ips-&gt;listIPs();
   $this-&gt;assertObjectHasAttribute("ipv4_cidrs", $ips);
   $this-&gt;assertObjectHasAttribute("ipv6_cidrs", $ips);
}</code></pre>
            <p>We are able to build a simple mock of our <code>Adapter</code> interface by using the standardised PSR-7 response format, when we do so we are able to define what parameters PHPUnit expects to be passed to this mock. With a mock <code>Adapter</code> class in place we are able to test the <code>IPs</code> Endpoint class as any if it was using a real HTTP client.</p>
    <div>
      <h3>Conclusions</h3>
      <a href="#conclusions">
        
      </a>
    </div>
    <p>Through building on modern versions of PHP, using good Object-Oriented Programming theory and allowing for effective testing we hope our PHP API binding provides a developer experience that is pleasant to build upon.</p><p>If you're interesting in helping improve the design of this codebase, I'd encourage you to take a look at the <a href="https://github.com/cloudflare/cloudflare-php">PHP API binding source code</a> on GitHub (and optionally give us a star).</p><p>If you work with Go or PHP and you're interested in helping Cloudflare turn our high-traffic customer-facing API into an ever more modern service-oriented environment; we're hiring for Web Engineers in <a href="https://boards.greenhouse.io/cloudflare/jobs/745994#.WcWKLtOGPys">San Francisco</a>, <a href="https://boards.greenhouse.io/cloudflare/jobs/682927#.WcWKPdOGPys">Austin</a> and <a href="https://boards.greenhouse.io/cloudflare/jobs/574636#.WcWKQdOGPys">London</a>.</p> ]]></content:encoded>
            <category><![CDATA[php]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Python]]></category>
            <guid isPermaLink="false">1NRqPHr4Wm8QNvqlHrAE4c</guid>
            <dc:creator>Junade Ali</dc:creator>
        </item>
        <item>
            <title><![CDATA[python-cloudflare]]></title>
            <link>https://blog.cloudflare.com/python-cloudflare/</link>
            <pubDate>Mon, 09 May 2016 22:47:07 GMT</pubDate>
            <description><![CDATA[ Very early on in the company’s history we decided that everything that CloudFlare does on behalf of its customer-base should be controllable via an API. In fact, when you login to the CloudFlare control panel, you’re really just making API calls to our backend services. ]]></description>
            <content:encoded><![CDATA[ 
    <div>
      <h3>Using the CloudFlare API via Python</h3>
      <a href="#using-the-cloudflare-api-via-python">
        
      </a>
    </div>
    <p>Very early on in the company’s history we decided that everything that CloudFlare does on behalf of its customer-base should be controllable via an <a href="https://www.cloudflare.com/learning/security/api/what-is-an-api/">API</a>. In fact, when you login to the CloudFlare control panel, you’re really just making API calls to our backend services. Over time that API has matured and improved. We are now on v4 of that API.</p><p>The current CloudFlare API is documented <a href="https://api.cloudflare.com">here</a> and it’s used by both the CloudFlare control panel and directly by umpteen customers every minute of every day. The new API is designed with a clean naming structure and consistent data representation for data. It’s also extensible.</p><p>This blog entry introduces <a href="https://github.com/cloudflare/python-cloudflare">python-cloudflare</a>, a Python wrapper providing full access to the CloudFlare v4 API.</p>
    <div>
      <h3>An example</h3>
      <a href="#an-example">
        
      </a>
    </div>
    <p>Let’s get right into the thick-of-it with the simplest coding example available to show python-cloudflare in action. This example lists all your domains (zones) and also checks some basic features for each zone.</p>
            <pre><code>#!/usr/bin/env python
import CloudFlare
def main():
    cf = CloudFlare.CloudFlare()
    zones = cf.zones.get(params={'per_page':50})
    for zone in zones:
        zone_name = zone['name']
        zone_id = zone['id']
        settings_ipv6 = cf.zones.settings.ipv6.get(zone_id)
        ipv6_on = settings_ipv6['value']
        print zone_id, ipv6_on, zone_name
    exit(0)
if __name__ == '__main__':
    main()</code></pre>
            <p>The structure of the CloudFlare class matches the API documentation. The <code>CloudFlare.zones.get()</code> method returns information about the zones as per the <a href="https://api.cloudflare.com/#zone-list-zones">list zones</a> documentation. The same for <code>CloudFlare.zones.settings.ipv6.get()</code> and its <a href="https://api.cloudflare.com/#zone-settings-get-ipv6-setting">documentation</a>.</p><p>Data is passed into the methods via standard Python structures and they are returned in Python structures that match the API documentation. That means if you see an API call in the documentation, then you can translate it into the Python code in a one-to-one manner.</p><p>For example, take a look at the <a href="https://api.cloudflare.com/#waf-rules-list-rules">WAF list rules</a> API call.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/58QXJkTf6PTJW4n69AsL1R/00031b7cac29a93d4da4d3816fd1171d/1-list_rules.png" />
            
            </figure><p>This codes into <code>CloudFlare.zones.dns_records.post()</code> method with the <code>zone_id</code> as the first argument, the <code>package_id</code> as the second argument and the optional parameters passed last (or if there aren’t any; then just drop the third argument for the call). Because this is a <code>GET</code> call there’s a <code>.get()</code> as part of the method name.</p>
            <pre><code>    r = cf.zones.firewall.waf.packages.rules.get(zone_id, package_id, params=params)</code></pre>
            <p>Here’s the much simpler <a href="https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record">Create DNS record</a> API call.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/giA1qSsZ55yOwwczVcXBk/5f7b3b2a9e56856ccd75af6e054791f2/2-create_dns_record.png" />
            
            </figure><p>This would be coded into the Python method <code>CloudFlare.zones.dns_records.post()</code> with the <code>zone_id</code> as the first argument and the required parameters passed as data. Because this is a <code>POST</code> call there’s a <code>.post()</code> as part of the method name.</p>
            <pre><code>    r = cf.zones.dns_records.post(zone_id, data=dns_record)</code></pre>
            <p>Here’s an example of that Create DNS record call in action. In this code, we add two records to an existing zone. We also show how the error is handled (in classic Python style).</p>
            <pre><code>    zone_name = 'example.com'
    try:
        r = cf.zones.get(params={'name': zone_name})
    except CloudFlare.CloudFlareAPIError as e:
        exit('/zones.get %s - %d %s' % (zone_name, e, e))
    except Exception as e:
        exit('/zones.get %s - %s' % (zone_name, e))
   zone_id = r['id']
   # DNS records to create
    dns_records = [
        {'name':'foo', 'type':'A',    'content':'192.168.100.100'}
        {'name':'foo', 'type':'AAAA', 'content':'2001:d8b::100:100'},
    ]
    for dns_record in dns_records:
        try:
            r = cf.zones.dns_records.post(zone_id, data=dns_record)
        except CloudFlare.CloudFlareAPIError as e:
            exit('/zones.dns_records.post %s - %d %s' % (record['name'], e, e))</code></pre>
            <p>There’s a whole folder of example code available on the GitHub repository.</p>
    <div>
      <h2>All on GitHub for anyone to use</h2>
      <a href="#all-on-github-for-anyone-to-use">
        
      </a>
    </div>
    <p>As we stated above (and as CloudFlare has done many times before) we have placed this code up on <a href="https://github.com/cloudflare/python-cloudflare">GitHub</a> for anyone to download and use. We welcome contributions and will review any pull requests. To install it, just clone it and follow the README instructions. For those that just want to get going right now; here’s the tl;dr install:</p>
            <pre><code>$ git clone https://github.com/cloudflare/python-cloudflare
$ cd python-cloudflare
$ ./setup.py build
$ sudo ./setup.py install</code></pre>
            
    <div>
      <h2>But wait; there’s more!</h2>
      <a href="#but-wait-theres-more">
        
      </a>
    </div>
    <p>Not only do you get the python API calls, you also get a fully functioning CLI (command line interface) that allows quick creation of scripts that interface with CloudFlare.</p><p>From the CLI command you can call any of the CloudFlare API calls. The command responds with the returned JSON data. If you want to filter the results you possibly also want to install the highly-versatile <a href="https://stedolan.github.io/jq/">jq</a> command. Here’s a command to check the nameservers for a specific domain hosted on CloudFlare and then process it via jq.</p>
            <pre><code>$ cli4 name=example.com /zones | jq -c '.[]|{"name_servers":.name_servers}'
{
  "name_servers":[
    "alice.ns.cloudflare.com",
    "bob.ns.cloudflare.com"
  ]
}
$</code></pre>
            <p>The CLI command will convert on-the-fly zone names into zone identifiers. For example; if you want to check the <a href="/dnssec-an-introduction/">DNSSEC</a> status on a zone your operate on CloudFlare; then use this command.</p>
            <pre><code>$ cli4 /zones/:example.com/dnssec | jq '{"status":.status,"ds":.ds}'
{
  "status": "active",
  "ds": "example.com. 3600 IN DS 2371 13 2 00000000000000000000000000000 ..."
}
$</code></pre>
            <p>You can issue <code>GET</code> <code>PUT</code> <code>POST</code> <code>PATCH</code> or <code>DELETE</code> calls into the API. You can pass data into a CloudFlare API call with the CLI command. All documented via the <code>README.md</code> and wiki examples in GitHub.</p><p>Here’s a useful command for customers that need to flush their cache files.</p>
            <pre><code>$ cli4 --delete purge_everything=true /zones/:example.com/purge_cache
{
  "id":"d8afaec3dd2b7f8c1b470e594a21a01d"
}
$</code></pre>
            <p>See how the commands arguments match the <a href="https://api.cloudflare.com/#zone-purge-all-files">purge_cache</a> API documentation.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3E5z7ixk8yXPm3sg4hYmGT/549732f7675c9a925056e141571ee9e6/3-purge.png" />
            
            </figure><p>Finally, here’s an example of turning on DNSSEC via the API.</p>
            <pre><code>$ cli4 --patch status=active /zones/:example.com/dnssec | jq -c '{"status":.status}'
{"status":"pending"}
$</code></pre>
            <p>There are plenty more examples available within the GitHub repo.</p>
    <div>
      <h3>CloudFlare API via other languages also available</h3>
      <a href="#cloudflare-api-via-other-languages-also-available">
        
      </a>
    </div>
    <p>Python isn’t the only language you can use to interact with CloudFlare’s API. If you’re a <code>Go</code>, or <code>Node.js</code> user, we also have client libraries you can use to interact with CloudFlare on our <a href="https://github.com/cloudflare/python-cloudflare">GitHub</a>. Find them here <a href="https://github.com/cloudflare/cloudflare-go">Go client</a> and here <a href="https://github.com/cloudflare/node-cloudflare">Node.js client</a>. Want to write something in a different language? Feel free to do that. The <a href="https://api.cloudflare.com/">API</a> spec is online and ready for you to code up.</p><p>If you like what you read here today and are interested in joining one of CloudFlare’s software teams, then checkout our <a href="http://www.cloudflare.com/join-our-team">Join Our Team</a> page.</p> ]]></content:encoded>
            <category><![CDATA[API]]></category>
            <category><![CDATA[WAF Rules]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[DNSSEC]]></category>
            <category><![CDATA[DNS]]></category>
            <category><![CDATA[Python]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">6BqiD9VfbJKeYp2SbfqE5w</guid>
            <dc:creator>Martin J Levy</dc:creator>
        </item>
    </channel>
</rss>