Skip to content

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.

ConstraintDetails
No filesystem persistenceThe WASM linear memory is reset per request. Use KV/R2/D1 for storage.
No network socketsStandard socket module is unavailable. Use pymode.tcp and pymode.http instead.
No threadsthreading is disabled (--disable-threads). Use pymode.parallel for real parallelism via child DOs.
No subprocessos.system, subprocess, os.fork are not available in WASI.
No signalsSignal handling is emulated (-lwasi-emulated-signal), limited functionality.
30s CPU limitPer-request CPU time is capped at 30 seconds (CF Workers limit).
128MB memoryPer-DO memory limit. Large data processing should use pymode.parallel to distribute across DOs.
10MB bundle sizeCF Workers’ paid-tier limit on compressed deploy. A typical pymode deploy produces a 6-8 MB gzipped wasm depending on which packages are AOT-preimported; the remainder is your project sources + site-packages. Heavy native deps (numpy/pandas) consume more of the budget.

PyMode bundles a subset of CPython’s stdlib as string constants in the worker. These are the modules available in the WASM runtime:

CategoryModules
Encodingsencodings, encodings.aliases, encodings.utf_8, encodings.ascii, encodings.latin_1
JSONjson
Regexre
Collectionscollections, collections.abc, functools, operator
Typesenum, types, typing, abc
Corewarnings, contextlib, dataclasses, copy, copyreg, weakref, _weakrefset
Textstring, textwrap
Cryptobase64, hashlib, hmac, secrets
Numbersrandom, bisect, heapq, numbers, fractions, decimal
Date/Timedatetime, calendar
Pathfnmatch, glob, pathlib
URLurllib, urllib.parse, ipaddress
Importimportlib, importlib.abc, importlib.machinery, importlib.util
Serializationpickle, _compat_pickle, struct
I/Oio, _pyio
Misckeyword, 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

ModuleImplementationNotes
binasciiPure Python (lib/polyfills/binascii.py)Replaces the C extension. Covers hex, base64, crc32, uu encoding.
socketPure Python (lib/polyfills/socket.py)Routes through pymode.tcp host imports. Provides constants, getaddrinfo, create_connection.
threadingPure Python (lib/polyfills/threading.py)No-op locks within a DO. Thread.start() spawns child DOs via pymode.parallel for real parallelism.
loggingPure Python (lib/polyfills/logging/)Threading-free logging that writes to stderr. Full API: getLogger, basicConfig, handlers.
multiprocessingPure Python (lib/polyfills/multiprocessing/)cpu_count() returns 1, exception classes. Use pymode.parallel for real parallelism.
sslPure Python (lib/polyfills/ssl.py)TLS termination handled by CF’s edge — Python code doesn’t need raw TLS.
zlibPure Python (lib/polyfills/zlib.py)Minimal compression polyfill for packages that import zlib at module level.

These stdlib modules require OS features that WASI doesn’t provide:

ModuleReason
socketPolyfilled — routes through pymode.tcp host imports.
sslPolyfilled — TLS termination handled by CF’s edge.
subprocessNo process spawning in WASI.
multiprocessingPolyfilled — cpu_count() returns 1. Use pymode.parallel for real parallelism via child DOs.
threadingPolyfilled — no-op locks within a DO. Thread.start() spawns child DOs via pymode.parallel.
sqlite3C extension not compiled for WASM. Use D1 binding instead.
ctypesNo dynamic linking for arbitrary shared libraries.
tkinterNo GUI in Workers.
cursesNo terminal in Workers.
readlineNo interactive terminal.
mmapNot available in WASI.
fcntl, termios, grp, pwdUnix-specific, not in WASI.
select, selectorsPolyfilled — selectors bundled in stdlib.
signalEmulated only, limited functionality.
resourceNo resource limits API in WASI.

These modules have been added to the stdlib bundle since initial release:

ModuleAdded for
html, html.parser, html.entitiesbeautifulsoup4, web scraping
email (full package)requests, urllib3, http.client
http, http.client, http.cookiejarrequests, httpx, urllib3
xml, xml.sax, xml.etree, xml.parserslangchain, defusedxml
asyncio (full package)pydantic, typing_extensions
argparsenumpy, click
localehttpx, distro
tempfile, shutilmany packages
importlib.metadata, importlib.resourcespackage metadata discovery
concurrent.futurestenacity, langsmith
zoneinfopydantic datetime
tomllibpydantic, project configs
configparservarious packages
sysconfigpydantic, setuptools
unittestpyparsing

Requesting a module: If you need a stdlib module that’s not bundled, open an issue or add it to the bundle list in scripts/generate-stdlib-fs.ts.

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, typing module

Not supported:

  • async/await at the handler level (use Asyncify for I/O instead)
  • asyncio event loop (no I/O multiplexing in WASI)

pymode deploy auto-detects when your project depends on a C-extension package and, if a variant wasm exists, uses that variant directly (fast deploy — no AOT rebuild). The numpy variant is wired and verified end-to-end as of this writing.

PackageHow it’s servedVerified deployable today
numpypython-numpy.wasm variant✅ (linalg stubbed)
Pillowpython-pillow.wasm variant
pydantic_corepython-pydantic-core.wasm variant
pyyamlpure-Python fallback (uv pip)
multidict / yarl / frozenlist / propcachepure-Python fallback (uv pip)likely (same pattern as pyyaml)
msgpackpure-Python fallback (uv pip)likely
markupsafestatically linkedyes
regex / xxhashstatically linkedyes
pandasneeds numpy + Cython compile passno
scipyneeds Fortran cross-compileno
scikit-learndepends on scipyno
lxmlneeds libxml2 + libxslt cross-compileno
cryptographyneeds OpenSSL cross-compileno
orjson / ujsonRust/C-only (no pure-Python fallback)no
cffiruntime C-compile model incompatible with WASMno

numpy caveats: the variant ships _multiarray_umath (array core + random + FFT) but does not ship _umath_linalg. Linear algebra functions in numpy.linalg are stubbed and raise ImportError if called. Everything else (arrays, math, FFT, random) works.

For projects needing pandas / scipy / scikit-learn / lxml / cryptography, Cloudflare’s official Python Workers ships pre-built wheels for those via Pyodide — that’s the working answer today. Closing pymode’s catalog gap is a multi-month engineering project; we do it package-by-package as needed.

These packages have conformance tests running in the actual workerd runtime:

PackageTypeTestsNotes
pydanticRust ext (compiled to WASM)4 passingpython-pydantic-core.wasm variant
fastapiPure Python (ASGI)1 passingRequest validation, typed APIs
langchain-corePure Python5 passingMessages, documents, utils, serialization
langgraphPure Python5 passingStateGraph, compile, invoke, conditional edges
instructorPure Python1 passingStructured LLM output via pydantic
openai SDKPure Python1 passingClient types importable
numpyC ext (compiled to WASM)8 passingpython-numpy.wasm variant, arrays, FFT, random
jinja2Pure Python3 passingTemplates, loops, filters, inheritance
requestsPure Python1 passingSession building, request construction
httpxPure Python1 passingRequest building, URL parsing
beautifulsoup4Pure Python2 passingHTML parsing, CSS selectors
pyyamlPure Python fallback2 passingYAML parse/dump
clickPure Python1 passingCLI command creation
attrsPure Python1 passingValidators, asdict
starlettePure Python1 passingASGI routing

The vitest suite covers these against the actual workerd runtime. Run npm test to see the current count and pass/fail status for your local environment.

These packages have native extensions replaced with pure-Python polyfills:

PackageOriginalPolyfill
uuid_utilsRust (_uuid_utils)stdlib uuid + time + os.urandom
xxhashC (_xxhash)hashlib.sha256 as stand-in
ormsgpackRust (ormsgpack)Wraps pure-Python msgpack
jiterRust (jiter.jiter)stdlib json
markupsafeC (_speedups)Pure Python fallback (built-in)
multiprocessingC (process model)Polyfill: cpu_count()=1, exception classes

A single Durable Object has a 128 MB memory budget. Most pure-Python packages run comfortably; CPython itself plus a typical handler costs ~20-40 MB depending on imports. Packages that allocate large arrays (pandas, numpy) reach the limit faster.

The pymode.parallel API spawns child DOs for compute parallelism — each child has its own memory budget. This pattern can in principle fan out work that’s too large for one DO, but isn’t automatic; you opt in by calling spawn() / gather() from your handler.

PackageReason
tensorflowNeeds GPU, ~500MB, C++/CUDA runtime
torchNeeds GPU, ~2GB, C++/CUDA runtime
scipyNeeds BLAS/LAPACK (Fortran), complex C

For ML inference, use Workers AI (env.AI.run()) instead.