Run reproducible browser Python with PyScript py-config package declarations
Run Python in the browser with reproducible dependencies by declaring packages in py-config before py-script execution. Covers prerequisites, config placement, validation checks, and recovery for missing Pyodide wheels.
05 Aug 2025, 16:19 UTC

Problem: browser Python that loads dependencies before your code runs
The practical goal is to execute Python in the browser without a server roundtrip, with dependencies declared up front so the same HTML produces the same interpreter state. PyScript achieves this by parsing a <py-config> element before any <py-script> runs and installing packages with micropip inside the Pyodide WebAssembly runtime.
Pyodide is a CPython build compiled to WebAssembly. Micropip is the in-browser package installer used by PyScript to fetch wheels. The useful takeaway is to declare packages in py-config and place the config element before scripts so installation happens during runtime initialization, not at import time.
Desired outcome
A static HTML file that loads PyScript from a CDN, declares a reproducible package list, and renders Python output in the page. No backend is required. Package resolution happens once per page load and the interpreter starts with sys.modules containing the declared packages.
Prerequisites
An HTML document served over HTTP or opened locally with ES module support. Modern Chromium-based browsers and Firefox support the required module loading.
Include the PyScript loader script in the document head. The loader pulls the PyScript runtime and Pyodide core. Version coupling matters: the PyScript release pins a Pyodide release, and API surface can change between releases.
Place a <py-config> element before any <py-script> element. PyScript parses the first config it encounters during initialization. If the config appears after a script, the script may execute before dependencies are installed.
Understand that packages install in the browser via micropip. Not all PyPI packages provide WebAssembly wheels. Pure Python packages are more likely to work than packages requiring native compilation outside Pyodide.
Focused procedure
Declare packages with py-config
Use JSON inside <py-config>. The minimal shape is a packages array with PyPI names.
<py-config>
{
"packages": ["numpy"]
}
</py-config>
Additional keys such as indexUrls can override package sources. Keep the list minimal to reduce download and memory use.
Load PyScript and write the script
Load the CDN script in head, then define config, then script. The output target is specified with the output attribute or by placing content after the script element.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<py-config>
{
"packages": ["numpy"]
}
</py-config>
<py-script output="out">
import sys
print("packages loaded:", "numpy" in sys.modules)
import numpy as np
print("numpy version available")
</py-script>
<div id="out"></div>
</body>
</html>
The runtime loads Pyodide, processes py-config, runs micropip installs for each package, then executes the script in order.
Execution flow to rely on
Browser parses HTML → PyScript loader initializes → py-config is read → Pyodide WebAssembly runtime boots → micropip installs declared packages → <py-script> executes with packages available in sys.modules.
Expected checks
Open browser dev tools and confirm the PyScript script loads without network errors for pyscript.js and pyscript.css.
Check the console for messages about config parsing. A missing or misplaced py-config typically produces a warning that config was not found before script execution.
Verify script output renders in the designated element, e.g., #out. A simple print statement is a reliable first check.
Inside the script, inspect sys.modules for the expected package name. Presence indicates the install step completed before import.
Recovery options for missing packages or load failures
If a package fails to load, first verify the PyPI name spelling matches the package name used by Pyodide. Hyphen vs underscore differences cause failures.
Confirm the package has a Pyodide wheel. Packages requiring compiled extensions may be absent from the Pyodide package index. Reduce the package list to a minimal reproducible set and test incrementally.
Add user-visible error handling around imports to avoid a blank page.
try:
import numpy as np
except Exception as e:
print("Import failed:", e)
For recovery after a failed load, reload the page after correcting py-config. Client-side state is not persistent across loads; there is no server rollback needed.
Limitations
Client-side execution increases initial download size and memory usage because Pyodide core and wheels are fetched to the browser.
PyScript version and Pyodide version are coupled. Changing the CDN URL changes both the runtime and available packages.
Not all PyPI packages have WebAssembly wheels, and import may fail at runtime even if the name is correct.
A practical way to check the result is to open dev tools, reload, and confirm the output element updates after the PyScript initialization completes, and that sys.modules contains the declared package name.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.