Limitations
PyMode runs upstream CPython 3.13 compiled to wasm32-wasi inside Cloudflare Workers Durable Objects. This works well for most Python code, but the WASM/WASI/DO environment has inherent constraints.
Platform Constraints
Section titled “Platform Constraints”| Constraint | Details |
|---|---|
| No filesystem persistence | The WASM linear memory is reset per request. Use KV/R2/D1 for storage. |
| No network sockets | Standard socket module is unavailable. Use pymode.tcp and pymode.http instead. |
| No threads | threading is disabled (--disable-threads). Use pymode.parallel for real parallelism via child DOs. |
| No subprocess | os.system, subprocess, os.fork are not available in WASI. |
| No signals | Signal handling is emulated (-lwasi-emulated-signal), limited functionality. |
| 30s CPU limit | Per-request CPU time is capped at 30 seconds (CF Workers limit). |
| 128MB memory | Per-DO memory limit. Large data processing should use pymode.parallel to distribute across DOs. |
| 10MB bundle size | Total compressed worker size. PyMode itself uses ~2-3MB gzipped, leaving ~7MB for your code and dependencies. |
Bundled Stdlib Modules
Section titled “Bundled Stdlib Modules”PyMode bundles a subset of CPython’s stdlib as string constants in the worker. These are the modules available in the WASM runtime:
Always Available (bundled in stdlib-fs)
Section titled “Always Available (bundled in stdlib-fs)”| Category | Modules |
|---|---|
| Encodings | encodings, encodings.aliases, encodings.utf_8, encodings.ascii, encodings.latin_1 |
| JSON | json |
| Regex | re |
| Collections | collections, collections.abc, functools, operator |
| Types | enum, types, typing, abc |
| Core | warnings, contextlib, dataclasses, copy, copyreg, weakref, _weakrefset |
| Text | string, textwrap |
| Crypto | base64, hashlib, hmac, secrets |
| Numbers | random, bisect, heapq, numbers, fractions, decimal |
| Date/Time | datetime, calendar |
| Path | fnmatch, glob, pathlib |
| URL | urllib, urllib.parse, ipaddress |
| Import | importlib, importlib.abc, importlib.machinery, importlib.util |
| Serialization | pickle, _compat_pickle, struct |
| I/O | io, _pyio |
| Misc | keyword, reprlib, traceback, linecache, tokenize, token, csv, pprint |
Built-in C Modules (compiled into python.wasm)
Section titled “Built-in C Modules (compiled into python.wasm)”These are C extension modules linked directly into the WASM binary:
array, _asyncio, _bisect, _contextvars, _csv, _heapq, _json, _pickle, _random, _struct, _zoneinfo, math, cmath, _statistics, _datetime, _decimal, _md5, _sha1, _sha2, _sha3, _blake2, _codecs, _collections, errno, _io, itertools, _sre, _thread, time, _typing, _weakref, _abc, _functools, _locale, _operator, _stat, _symtable, unicodedata, pyexpat, _elementtree
Polyfilled Modules
Section titled “Polyfilled Modules”| Module | Implementation | Notes |
|---|---|---|
binascii | Pure Python (lib/polyfills/binascii.py) | Replaces the C extension. Covers hex, base64, crc32, uu encoding. |
socket | Pure Python (lib/polyfills/socket.py) | Routes through pymode.tcp host imports. Provides constants, getaddrinfo, create_connection. |
threading | Pure Python (lib/polyfills/threading.py) | No-op locks within a DO. Thread.start() spawns child DOs via pymode.parallel for real parallelism. |
logging | Pure Python (lib/polyfills/logging/) | Threading-free logging that writes to stderr. Full API: getLogger, basicConfig, handlers. |
multiprocessing | Pure Python (lib/polyfills/multiprocessing/) | cpu_count() returns 1, exception classes. Use pymode.parallel for real parallelism. |
ssl | Pure Python (lib/polyfills/ssl.py) | TLS termination handled by CF’s edge — Python code doesn’t need raw TLS. |
zlib | Pure Python (lib/polyfills/zlib.py) | Minimal compression polyfill for packages that import zlib at module level. |
Not Available
Section titled “Not Available”These stdlib modules require OS features that WASI doesn’t provide:
| Module | Reason |
|---|---|
socket | Polyfilled — routes through pymode.tcp host imports. |
ssl | Polyfilled — TLS termination handled by CF’s edge. |
subprocess | No process spawning in WASI. |
multiprocessing | Polyfilled — cpu_count() returns 1. Use pymode.parallel for real parallelism via child DOs. |
threading | Polyfilled — no-op locks within a DO. Thread.start() spawns child DOs via pymode.parallel. |
sqlite3 | C extension not compiled for WASM. Use D1 binding instead. |
ctypes | No dynamic linking for arbitrary shared libraries. |
tkinter | No GUI in Workers. |
curses | No terminal in Workers. |
readline | No interactive terminal. |
mmap | Not available in WASI. |
fcntl, termios, grp, pwd | Unix-specific, not in WASI. |
select, selectors | Polyfilled — selectors bundled in stdlib. |
signal | Emulated only, limited functionality. |
resource | No resource limits API in WASI. |
Now Bundled (previously missing)
Section titled “Now Bundled (previously missing)”These modules have been added to the stdlib bundle since initial release:
| Module | Added for |
|---|---|
html, html.parser, html.entities | beautifulsoup4, web scraping |
email (full package) | requests, urllib3, http.client |
http, http.client, http.cookiejar | requests, httpx, urllib3 |
xml, xml.sax, xml.etree, xml.parsers | langchain, defusedxml |
asyncio (full package) | pydantic, typing_extensions |
argparse | numpy, click |
locale | httpx, distro |
tempfile, shutil | many packages |
importlib.metadata, importlib.resources | package metadata discovery |
concurrent.futures | tenacity, langsmith |
zoneinfo | pydantic datetime |
tomllib | pydantic, project configs |
configparser | various packages |
sysconfig | pydantic, setuptools |
unittest | pyparsing |
Requesting a module: If you need a stdlib module that’s not bundled, open an issue or add it to
BOOT_FILESinscripts/generate-stdlib-fs.py.
Python Language Support
Section titled “Python Language Support”Full CPython 3.13 language support including:
- All syntax features (match/case, walrus operator, f-strings, etc.)
- Generators, async generators, coroutines
- Decorators, metaclasses, descriptors
- Exception groups,
ExceptionGroup - Type hints,
typingmodule
Not supported:
async/awaitat the handler level (use Asyncify for I/O instead)asyncioevent loop (no I/O multiplexing in WASI)
Package Support
Section titled “Package Support”Tested & Working
Section titled “Tested & Working”These packages have conformance tests running in the actual workerd runtime:
| Package | Type | Tests | Notes |
|---|---|---|---|
| pydantic | Rust ext (compiled to WASM) | 4 passing | python-pydantic-core.wasm variant |
| fastapi | Pure Python (ASGI) | 1 passing | Request validation, typed APIs |
| langchain-core | Pure Python | 5 passing | Messages, documents, utils, serialization |
| langgraph | Pure Python | 5 passing | StateGraph, compile, invoke, conditional edges |
| instructor | Pure Python | 1 passing | Structured LLM output via pydantic |
| openai SDK | Pure Python | 1 passing | Client types importable |
| numpy | C ext (compiled to WASM) | 8 passing | python-numpy.wasm variant, arrays, FFT, random |
| jinja2 | Pure Python | 3 passing | Templates, loops, filters, inheritance |
| requests | Pure Python | 1 passing | Session building, request construction |
| httpx | Pure Python | 1 passing | Request building, URL parsing |
| beautifulsoup4 | Pure Python | 2 passing | HTML parsing, CSS selectors |
| pyyaml | Pure Python fallback | 2 passing | YAML parse/dump |
| click | Pure Python | 1 passing | CLI command creation |
| attrs | Pure Python | 1 passing | Validators, asdict |
| starlette | Pure Python | 1 passing | ASGI routing |
Total: 261 tests across all test suites.
Polyfilled C/Rust Extensions
Section titled “Polyfilled C/Rust Extensions”These packages have native extensions replaced with pure-Python polyfills:
| Package | Original | Polyfill |
|---|---|---|
uuid_utils | Rust (_uuid_utils) | stdlib uuid + time + os.urandom |
xxhash | C (_xxhash) | hashlib.sha256 as stand-in |
ormsgpack | Rust (ormsgpack) | Wraps pure-Python msgpack |
jiter | Rust (jiter.jiter) | stdlib json |
markupsafe | C (_speedups) | Pure Python fallback (built-in) |
multiprocessing | C (process model) | Polyfill: cpu_count()=1, exception classes |
Architecture: Each Package Gets Its Own DO
Section titled “Architecture: Each Package Gets Its Own DO”Memory constraints (128MB Worker / 256MB DO) apply per DO, not globally. Heavy packages run in dedicated DOs communicating via Worker RPC:
Orchestrator DO Package DOs (each 256MB)┌────────────┐ ┌─────────────┐│ Your app │───RPC───▶│ numpy DO ││ routing │ └─────────────┘│ logic │───RPC───▶│ pandas DO ││ │ └─────────────┘└────────────┘───RPC───▶│ langchain │ │ DO │ └─────────────┘This means even pandas (~100MB) can work — it just needs its own DO. The threading.Thread polyfill already spawns child DOs via pymode.parallel.
Won’t Work in WASM (any DO)
Section titled “Won’t Work in WASM (any DO)”| Package | Reason |
|---|---|
| tensorflow | Needs GPU, ~500MB, C++/CUDA runtime |
| torch | Needs GPU, ~2GB, C++/CUDA runtime |
| scipy | Needs BLAS/LAPACK (Fortran), complex C |
For ML inference, use Workers AI (env.AI.run()) instead.