Cloudflare WorkersのPython WorkerがGAされたので試してみる

Cloudflare WorkersのPython WorkerがGAされたので試してみる

Cloudflare WorkersでPython Workerがリリースされたので、実際に試してみました。requestsとfeedparserを使ってRSSフィードを取得・パースするWorkerを作成し、デプロイするまでの流れを紹介します。
2026.09.28

こんばんは、情報システム室の夏目です。

先日Cloudflare WorkersでPython WorkerがGAされました。

https://blog.cloudflare.com/python-workers-ga/

今回はこれを試してみようと思います。

Python Worker

https://developers.cloudflare.com/workers/languages/python/how-python-workers-work/

Python Workerでは、WebAssemblyにコンパイルされたCPython実装である Pyodide で実行されます。

そのため、標準ライブラリの一部や pypi.org で公開されているパッケージが全て使えるわけではありません。

pypi.orgで公開されている Pure Python なパッケージや、 PyEmscriptenに対応したパッケージを使用できると記載されています。
また、Pyodideに標準で含まれているパッケージもサポートされているとのことです。

https://developers.cloudflare.com/workers/languages/python/packages/#supported-libraries
Python packages supported in Cloudflare Workers · Cloudflare Workers docs - Supported Libraries

https://pyodide.org/en/stable/usage/packages-in-pyodide.html
Packages built in Pyodide — Version 314.0.7

ただし、Pure PythonであったりPyodideに標準に含まれているパッケージでも使用できないことがあります。

例えばPython用AWS SDKの boto3 は依存関係も含めてPure Pythonのパッケージであり、ネットワーク通信周りで使用している urllib3 はPyodideに標準で含まれているライブラリです。

しかし、urllib3のsslのimport周りでエラーが起きて使用できませんでした。

✘ [ERROR] A request to the Cloudflare API (/accounts/6c0ed76030fe097d9fb9a4cd29a874af/workers/scripts/trial-python-worker/versions) failed.

  Uncaught Error: PythonError: Traceback (most recent call last):
    File "/lib/python314.zip/_pyodide/_base.py", line 666, in pyimport_impl
      res = __import__(stem, fromlist=fromlist)
    File "/session/metadata/entry.py", line 5, in <module>
      from boto3 import Session
    File "/session/metadata/python_modules/boto3/__init__.py", line 17, in <module>
      from boto3.compat import _warn_deprecated_python
    File "/session/metadata/python_modules/boto3/compat.py", line 21, in <module>
      from s3transfer.manager import TransferConfig
    File "/session/metadata/python_modules/s3transfer/manager.py", line 25, in <module>
      from s3transfer.copies import CopySubmissionTask
    File "/session/metadata/python_modules/s3transfer/copies.py", line 20, in <module>
      from s3transfer.tasks import (
      ...<4 lines>...
      )
    File "/session/metadata/python_modules/s3transfer/tasks.py", line 16, in <module>
      from s3transfer.utils import get_callbacks
    File "/session/metadata/python_modules/s3transfer/utils.py", line 29, in <module>
      from botocore.httpchecksum import DEFAULT_CHECKSUM_ALGORITHM, AwsChunkedWrapper
    File "/session/metadata/python_modules/botocore/httpchecksum.py", line 34, in <module>
      from botocore.model import StructureShape
    File "/session/metadata/python_modules/botocore/model.py", line 18, in <module>
      from botocore.auth import resolve_auth_type
    File "/session/metadata/python_modules/botocore/auth.py", line 46, in <module>
      from botocore.utils import (
      ...<3 lines>...
      )
    File "/session/metadata/python_modules/botocore/utils.py", line 40, in <module>
      import botocore.httpsession
    File "/session/metadata/python_modules/botocore/httpsession.py", line 24, in <module>
      from urllib3.util.ssl_ import (
      ...<6 lines>...
      )
  ImportError: cannot import name 'ssl' from 'urllib3.util.ssl_'
  (/session/metadata/python_modules/urllib3/util/ssl_.py)

    at null.<anonymous> (pyodideRuntime-internal:emscriptenSetup:21276:12) in new_error
    at [object Object] in $wrap_exception
    at [object Object] in $pythonexc2js
    at null.<anonymous> (pyodideRuntime-internal:emscriptenSetup:24011:97) in
  callPyObjectKwargsPromising
   [code: 10021]

使用するライブラリについては慎重に選択する必要があります。

Python Workerを試してみる

今回は、Pyodideに含まれていた requests とPure Pythonなパッケージの feedparser を使って、Python Workerを試してみます。

requestsでRSSフィードを取得し、feedparserでパースします。

1. プロジェクトを作成する

まずはpywranglerを使用してプロジェクトを作成します。

$ uvx --from workers-py pywrangler init
INFO     Passing command to npx create-cloudflare: npx --yes create-cloudflare --lang=python --no-deploy
(node:51495) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
(Use `node --trace-warnings ...` to show where the warning was created)

──────────────────────────────────────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.72.13!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.

Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
──────────────────────────────────────────────────────────────────────────────────────────────────────────

╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./trial-python-worker
│
├ What would you like to start with?
│ category Hello World example
│
├ Which template would you like to use?
│ type Worker only
│
├ Which language do you want to use?
│ lang Python (beta)
│
├ Copying template files
│ files copied to project directory
│
├ Updating name in `package.json`
│ updated `package.json`
│
├ Updating name in `pyproject.toml`
│ updated `pyproject.toml`
│
├ Installing dependencies
│ installed via `npm install`
│
├ Do you want to add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?
│ no agents
│
╰ Application created

╭ Configuring your application for Cloudflare Step 2 of 3
│
├ Installing wrangler A command line tool for building Cloudflare Workers
│ installed via `npm install wrangler --save-dev`
│
├ Selecting workerd compatibility date
│ compatibility date 2026-09-25
│
├ Do you want to use git for version control?
│ no git
│
╰ Application configured

╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
│
╰ Done

────────────────────────────────────────────────────────────
🎉  SUCCESS  Application created successfully!

💻 Continue Developing
Change directories: cd trial-python-worker
Deploy: npm run deploy

📖 Explore Documentation
https://developers.cloudflare.com/workers

🐛 Report an Issue
https://github.com/cloudflare/workers-sdk/issues/new/choose

💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────

プロジェクトが作成されたので中身を見てみます。

$ cd trial-python-worker
$ eza -la --time-style=long-iso -F=auto
.rw-r--r--@  761 natsume.yuta 2026-09-28 16:19 .gitignore
.rw-r--r--@    4 natsume.yuta 2026-09-28 16:19 .python-version
drwxr-xr-x@    - natsume.yuta 2026-09-28 16:19 .vscode/
drwxr-xr-x@    - natsume.yuta 2026-09-28 16:19 node_modules/
.rw-r--r--@  45k natsume.yuta 2026-09-28 16:19 package-lock.json
.rw-r--r--@  266 natsume.yuta 2026-09-28 16:19 package.json
.rw-r--r--@  230 natsume.yuta 2026-09-28 16:19 pyproject.toml
.rw-r--r--@  751 natsume.yuta 2026-09-28 16:19 README.md
drwxr-xr-x@    - natsume.yuta 2026-09-28 16:19 src/
.rw-r--r--@ 1.5k natsume.yuta 2026-09-28 16:19 wrangler.jsonc

$ eza -laT --time-style=long-iso -F=auto src
drwxr-xr-x@   - natsume.yuta 2026-09-28 16:19 src/
.rw-r--r--@ 201 natsume.yuta 2026-09-28 16:19 ├── entry.py
.rw-r--r--@  50 natsume.yuta 2026-09-28 16:19 └── submodule.py

entry.py と submodule.py, .python-version, pyproject.toml は次のようになっています。

src/entry.py
from workers import Response, WorkerEntrypoint
from submodule import get_hello_message
class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response(get_hello_message())
src/submodule.py
def get_hello_message():
    return "Hello World!"
.python-version
3.12
pyproject.toml
[project]
name = "trial-python-worker"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

[dependency-groups]
dev = ["workers-py", "workers-runtime-sdk"]

デフォルトではPython3.12環境で作成され、Python Worker用の依存ライブラリも入っているようです。
Python Worker用の依存ライブラリはdependency-groupが dev になっているので、デプロイ時に自動で組み込んでくれると思われます。

また、基本的にはTypeScript(JS)のWorkerのときと同様の構造のようです。

2. 環境を整える

プロジェクトの作成ができたので少し環境を整えます。

最初にPythonのバージョンを3.14系にしてみます。

.python-version
3.14

次に、今回使用する requests と feedparser のインストールします。

$ uv add requests==2.33.1 feedparser==6.0.14
Using CPython 3.12.14
Creating virtual environment at: .venv
Resolved 20 packages in 1.19s
Prepared 1 package in 381msInstalled 7 packages in 8ms
 + certifi==2026.7.22
 + charset-normalizer==3.5.1
 + feedparser==6.0.14
 + feedparser-sgmllib==2.1.0
 + idna==3.20
 + requests==2.33.1
 + urllib3==2.8.0

最後に、wrangler.jsoncのvarsにRSSフィードのURLを追加し、補完が効くようにします。

wrangler.jsonc
/**
 * For more details on how to configure Wrangler, refer to:
 * https://developers.cloudflare.com/workers/wrangler/configuration/
 */
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "trial-python-worker",
  "main": "src/entry.py",
  "compatibility_date": "2026-09-25",
  "compatibility_flags": [
    "python_workers"
  ],
  "observability": {
    "enabled": true
  },
  /**
   * Environment Variables
   * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
   * Note: Use secrets to store sensitive data.
   * https://developers.cloudflare.com/workers/configuration/secrets/
   */
  "vars": {
    // feedparserのRSS FeedのURL
    "FEED_URL": "https://pypi.org/rss/project/feedparser/releases.xml"
  }
}

wrangler.jsoncの末尾にvarsを足しました。
次に pywrangler types を実行してIDEでの補完が効くようにします。

$ uv run pywrangler types

 ⛅ wrangler 4.142.0
────────────────────
Generating project types...

interface __BaseEnv_Env {
        FEED_URL: "https://pypi.org/rss/project/feedparser/releases.xml";
}
declare namespace Cloudflare {
        interface GlobalProps {
                mainModule: typeof import("../../../../../../Users/natsume.yuta/spaces/work/blog/021_cloudflare_python_worker/trial-python-worker/src/entry");
        }
        interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}
type StringifyValues<EnvType extends Record<string, unknown>> = {
        [Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
};
declare namespace NodeJS {
        interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "FEED_URL">> {}
}

Generating runtime types...

Runtime types generated.

────────────────────────────────────────────────────────────
✨ Types written to /var/folders/mr/ntgnwdwx4957dwl5zx2vwy540000gn/T/tmpa_eqdo2t/worker-configuration.d.ts

Action required Install @types/node
Since your Worker has Node.js compatibility enabled, you should install Node.js types by running "npm i --save-dev @types/node".

📖 Read about runtime types
https://developers.cloudflare.com/workers/languages/typescript/#generate-types
📣 Remember to rerun 'wrangler types' after you change your wrangler.jsonc file.

added 1 package, and audited 3 packages in 583ms

found 0 vulnerabilities

$ eza -laT --time-style=long-iso -F=auto src
drwxr-xr-x@    - natsume.yuta 2026-09-28 16:44 src/
.rw-r--r--@  201 natsume.yuta 2026-09-28 16:19 ├── entry.py
drwxr-xr-x@    - natsume.yuta 2026-09-28 16:44 ├── js-stubs/
.rw-r--r--@ 824k natsume.yuta 2026-09-28 16:44 │   └── __init__.pyi
.rw-r--r--@   50 natsume.yuta 2026-09-28 16:19 └── submodule.py

src/js-stubs/__init__.pyi が作成されました。
824kbもあるので中身を見ませんが、これでIDEでの補完が効くようになります。

3. 実装する

今回は次のようにentry.pyを編集します。

src/entry.py
import os
import sys

import feedparser
import requests
from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        resp = requests.get(self.env.FEED_URL)

        feed = feedparser.parse(resp.text)

        data = {
            "python_version": sys.version,
            "environment_variables": {k: v for k, v in os.environ.items()},
            "feed": feed,
        }

        return Response.json(data)

レスポンスには、pythonのバージョンと実行時の環境変数、パースしたRSS Feedの三つを返すようにしています。

varsに追加した FEED_URL はCloudflareのドキュメントにあったように self.env.FEED_URL でアクセスしています。
wrangler.jsoncにはEnvironment Variablesとの記載があったので環境変数に含まれているかも確認します。

4. デプロイ

pywrangler deploy でデプロイします。

$ uv run pywrangler deploy
INFO     Resolved 8 requirements from /Users/natsume.yuta/spaces/work/blog/021_cloudflare_python_worker/trial-python-worker/pylock.toml.                                                                                            
INFO     Installing packages into python_modules...                                                                                                                                                                                 
INFO     Packages installed in python_modules.                                                                                                                                                                                      
INFO     Installing packages into .venv-workers...                                                                                                                                                                                  
INFO     Packages installed in .venv-workers.                                                                                                                                                                                       
INFO     Passing command to npx wrangler: npx --yes wrangler deploy                                                                                                                                                                 

 ⛅️ wrangler 4.142.0
────────────────────
Attaching additional modules:
┌─────────────────────┬────────┬─────────────┐
│ Name                │ Type   │ Size        │
├─────────────────────┼────────┼─────────────┤
│ submodule.py        │ python │ 0.05 KiB    │
├─────────────────────┼────────┼─────────────┤
│ Vendored Modules    │        │ 2032.07 KiB │
├─────────────────────┼────────┼─────────────┤
│ Total (203 modules) │        │ 2032.12 KiB │
└─────────────────────┴────────┴─────────────┘
Total Upload: 2032.58 KiB / gzip: 584.72 KiB
Worker Startup Time: 1441 ms
Your Worker has access to the following bindings:
Binding                                                        Resource                  
env.FEED_URL ("https://pypi.org/rss/project/feedpars...")      Environment Variable      

Uploaded trial-python-worker (9.87 sec)
Deployed trial-python-worker triggers (0.83 sec)
  https://trial-python-worker.luciferous.workers.dev
Current Version ID: efeb63e0-c87e-4fd7-9d58-27d8cc410730

5. curlで叩いてみる

curlで叩いて結果を取得します。
フィードの中身が大きかったので一旦ファイルに書き出します。

$ curl -o response.json https://trial-python-worker.luciferous.workers.dev
response.json
{
  "python_version": "3.14.2 (main, Aug 25 2026, 05:50:55) [Clang 23.0.0git (https:/github.com/llvm/llvm-project e5927fecf8a6ce89e1a4eac5b8",
  "environment_variables": {
    "USER": "web_user",
    "LOGNAME": "web_user",
    "PATH": "/",
    "PWD": "/",
    "HOME": "/session",
    "LANG": "en.UTF-8",
    "_": "./this.program",
    "LD_LIBRARY_PATH": "/usr/lib:/lib/python3.14/site-packages:/session/metadata/python_modules/lib/",
    "PYTHONHASHSEED": "111"
  },
  "feed": {
    "bozo": false,
    "entries": [
      {
        "title": "6.0.14",
        "title_detail": {
          "type": "text/plain",
          "language": null,
          "base": "",
          "value": "6.0.14"
        },
        "links": [
          {
            "rel": "alternate",
            "type": "text/html",
            "href": "https://pypi.org/project/feedparser/6.0.14/"
          }
        ],
        "link": "https://pypi.org/project/feedparser/6.0.14/",
        "summary": "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds",
        "summary_detail": {
          "type": "text/html",
          "language": null,
          "base": "",
          "value": "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds"
        },
        "authors": [
          {
            "email": "contactme@kurtmckee.org"
          }
        ],
        "author": "contactme@kurtmckee.org",
        "author_detail": {
          "email": "contactme@kurtmckee.org"
        },
        "published": "Thu, 30 Jul 2026 14:07:39 GMT",
        "published_parsed": [
          2026,
          7,
          30,
          14,
          7,
          39,
          3,
          211,
          0
        ]
      },
      ... 
    ],
    "feed": {
      "title": "PyPI recent updates for feedparser",
      "title_detail": {
        "type": "text/plain",
        "language": null,
        "base": "",
        "value": "PyPI recent updates for feedparser"
      },
      "links": [
        {
          "rel": "alternate",
          "type": "text/html",
          "href": "https://pypi.org/project/feedparser/"
        }
      ],
      "link": "https://pypi.org/project/feedparser/",
      "subtitle": "Recent updates to the Python Package Index for feedparser",
      "subtitle_detail": {
        "type": "text/html",
        "language": null,
        "base": "",
        "value": "Recent updates to the Python Package Index for feedparser"
      },
      "language": "en"
    },
    "headers": {},
    "encoding": "utf-8",
    "version": "rss20",
    "namespaces": {}
  }
}

非常に長かったのでentriesは最初の一件だけにしています。

この結果を見ると、以下のことがわかります。

  • requestsによるFeed本文の取得とfeedparserによるパースができている
  • .python-version で 3.14 としたことが効いている
  • vars に記載したものはPython Workerでは環境変数としては取得できない
    • TypeScriptの場合は環境変数としても取得できる

まとめ

以上、先日GAされたCloudflare WorkersのPython Workerを試す話でした。

何かのお役に立てたら幸いです。

この記事をシェアする

関連記事