Declarative Python Dependencies in PyScript with <py-config>
PyScript's <py-config> tag declares Python packages in TOML/JSON; micropip installs WASM wheels at load time, caching them in IndexedDB. Works for pure-Python and Pyodide-built binaries, but lacks full dependency resolution and fails on missing wheels.
01 Jul 2026, 08:05 UTC

What Does
PyScript's <py-config> tag lets you declare Python package dependencies in a TOML or JSON block inside your HTML. At page load, PyScript hands the list to micropip, which fetches pre‑built WebAssembly wheels from PyPI or the Pyodide repository and installs them before any <py-script> or <py-repl> code runs. Installed wheels are cached in the browser's IndexedDB, so repeat visits skip the download.
Worked Example
Create a minimal HTML file served over HTTP (browsers block micropip on file://):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PyScript config demo</title>
<link rel="stylesheet" href="https://pyscript.net/releases/2024.1.1/core.css">
<script type="module" src="https://pyscript.net/releases/2024.1.1/core.js"></script>
</head>
<body>
<py-config type="toml">
packages = ["requests==2.31.0", "beautifulsoup4"]
</py-config>
<py-script>
import requests
import bs4
print(f"requests {requests.__version__}")
print(f"beautifulsoup4 {bs4.__version__}")
</py-script>
</body>
</html>
Serve it:
python -m http.server 8000
Open http://localhost:8000. The browser console shows the two version strings after a short install pause.
How the Mechanism Works
- Parse: PyScript reads the
<py-config>block before initializing the Python runtime. - Resolve:
micropiprequests each package name (with optional version pin) from the Pyodide package index. - Fetch: Wheels compiled for the
wasm32-emscriptentarget are streamed into the browser. - Install: Packages are unpacked into a virtual filesystem backed by IndexedDB.
- Execute: Only after all declared packages report success does PyScript run your
<py-script>code.
Pure‑Python packages (e.g., requests, beautifulsoup4) install reliably. Packages with C extensions (e.g., numpy, pandas) work only when the Pyodide project provides a pre‑built wheel; check the official list before assuming availability.
Supported Configuration Keys
| Key | Type | Purpose |
|---|---|---|
packages | array of strings | PyPI package names, optional version pins ("requests==2.31.0") |
paths | array of strings | Local pure‑Python directories or .py files (e.g., "./my_lib") |
js_modules | array of strings | npm packages loaded via esm.sh (e.g., ["lodash"]) |
All three keys can appear in the same <py-config> block, enabling mixed Python/JS dependency graphs.
Limits and Common Mistakes
No Full Dependency Resolution
micropip installs exactly what you list. It does not build a lockfile or reconcile transitive dependencies. If package-a==1.0 requires package-b>=2.0 but you also pin package-b==1.5, the install succeeds but runtime imports may fail. Test the combination in a real browser session.
Binary‑Compatibility Boundary
Wheels must target Pyodide's WASM32‑Emscripten build. Many popular scientific packages have official builds, but others (e.g., cryptography, python-magic) do not. Attempting to install an unavailable wheel yields a console error like No matching distribution found for ....
Cache Staleness
IndexedDB caches wheels keyed by the exact package list. Changing a version pin triggers a fresh download, but a network failure mid‑install can leave the cache in a partial state. A hard refresh (Shift+F5) clears the cache and forces a clean retry.
Load‑Time Penalty
Large wheels (SciPy ~50 MB, pandas ~30 MB) add seconds to first load and increase memory pressure. For optional features, prefer lazy installation inside Python code:
<py-script>
import asyncio, micropip
async def load_heavy():
await micropip.install('scipy')
import scipy
return scipy
</py-script>
Tag Order Matters
The <py-config> tag must appear before any <py-script> or <py-repl> tags. If placed after, PyScript initializes without the declared packages and raises ModuleNotFoundError.
Version‑Specific Syntax
PyScript 2024.1+ supports TOML and JSON. Earlier versions accepted JSON only. Older tutorials may show type="json" with a different structure; verify your PyScript version with:
<py-script>
import pyscript
print(pyscript.__version__)
</py-script>
Verification Checklist
- Open DevTools → Console: confirm version prints appear after install messages.
- DevTools → Application → IndexedDB →
pyodide→packages: verify cached.whlfiles persist across reloads. - Test a known‑missing wheel (e.g.,
packages = ["python-magic"]) to see the binary‑compatibility error. - Change a version pin, reload, and watch the new wheel download.
When to Use This Approach
Declarative <py-config> is ideal for small‑to‑medium dependency sets that are required on every page load. For heavy or conditional dependencies, move installation into Python code with await micropip.install() to keep the initial payload light.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.