Cloudflare WorkersのPython WorkerがGAされたので試してみる
こんばんは、情報システム室の夏目です。
先日Cloudflare WorkersでPython WorkerがGAされました。
今回はこれを試してみようと思います。
Python Worker
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 は次のようになっています。
from workers import Response, WorkerEntrypoint
from submodule import get_hello_message
class Default(WorkerEntrypoint):
async def fetch(self, request):
return Response(get_hello_message())
def get_hello_message():
return "Hello World!"
3.12
[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系にしてみます。
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を追加し、補完が効くようにします。
/**
* 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を編集します。
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
{
"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を試す話でした。
何かのお役に立てたら幸いです。






