Created by Anaconda and launched in April 2022, PyScript is an experimental but promising new technology that makes the Python runtime available as a scripting language in WebAssembly-enabled browsers.
Every commonly used browser now supports WebAssembly, the high-speed runtime standard that languages like C, C++, and Rust can compile to. Python’s reference implementation is written in C, and one earlier project, Pyodide, provided a WebAssembly port of the Python runtime.
PyScript, though, aims to provide a whole in-browser environment for running Python as a web scripting language. It builds on top of Pyodide but adds or enhances features like the ability to import modules from the standard library, use third-party imports, configure two-way interactions with the Document Object Model (DOM), and do many other things useful in both the Python and JavaScript worlds.
Right now, PyScript is still a prototypical and experimental project. Anaconda doesn’t recommend using it in production. But curious users can try out examples on the PyScript site and use the available components to build experimental Python-plus-JavaScript applications in the browser.
In this article, we’ll take a tour of PyScript, and see how it facilitates Python and JavaScript interactions in your web apps.
Programming with PyScript
At its core, PyScript consists of a single JavaScript include that you can add to a web page. This include loads the base PyScript runtime and automatically adds support for custom tags used in PyScript.
Here’s a simple example of a “Hello, world” project in PyScript:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet"
href="https://pyscript.net/releases/2023.11.2/core.css" />
<script type="module"
src="https://pyscript.net/releases/2023.11.2/core.js">
</script>
</head>
<body>
<script type="py" terminal>
from pyscript import display
display("Hello World!")
print("Hello terminal!")
</script>
</body>
</html>
The script
tag in the document’s head
loads the core PyScript functionality. The .css
stylesheet is optional, but useful. Among other things, it inserts notices to the user at the page’s load time about what the page is doing—loading the Python runtime, initializing, and so on.
Python code is enclosed in the script
tag with a type="py"
attribute. Note that the code should be formatted according to Python’s conventions for indentation, or it won’t run properly. Be aware of this if you use an editor that reformats HTML automatically; it might mangle the contents of the script
block and make it unrunnable. You can also refer to a .py
file rather than include the script inline, which may be easier.
Any Python code is evaluated once the PyScript components finish loading. You can choose whether the output is sent to the DOM (use pyscript.display
), or to an embedded terminal. If you use the terminal, you have to include terminal
as an attribute on the script
tag. (More about this below.)
If the script in the tags writes to stdout
(as with a print
statement), you can direct where you want the output displayed on the page by supplying an output
property. In this example, stdout
for the script gets directed to the div
with the ID of "out"
.
If you save this into a file and open it in a web browser, you’ll first see a “loading” indicator and a pause, as the browser obtains the PyScript runtime and sets it up. The runtime should remain cached on future loads but will still take a moment to activate. After that, Hello world
should appear on the page twice—once at the top in HTML, and once in a black pane that is the embedded terminal.
Standard library imports
Scripts using Python’s builtins alone are only somewhat useful. Python’s standard library is available in PyScript the same way you’d use it in regular Python: simply import
and get to work. Standard library imports should just work in PyScript.
If you wanted to modify the above script block to display the current time, you wouldn’t need to do it any differently than you would in conventional Python:
import datetime
print ("Current date and time:",
datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
Using libraries from PyPI
What if you want to install a package from PyPI and use that? PyScript lets you specify project configurations, including any third-party packages to be installed from PyPI, by way of a .toml
or .json
format file in your project’s directory. Let’s see how it works using .toml
.
To use the project config file, you’ll need to include the config
directive in your script
tag:
<script type="py" src="main.py" config="pyscript.toml">
The pyscript.toml
file lists any needed packages:
packages = ["package","another-package"]
Note that not all packages from PyPI will install and run as expected. Most “pure” Python packages, like humanize
, should run fine. And packages used in the examples provided by Anaconda, like numpy
, pandas
, bokeh
, or matplotlib
, will also work. But packages that require network access or work with platform-native elements like GUIs are not likely to work.
Importing locally
For another common scenario, let’s say you want to import from other Python scripts in the same directory tree as your web page. Using imports makes it easier to move more of your Python logic out of the web page itself, where it’s intermixed with your presentation and may become difficult to work with.
Normally, Python uses the presence of other .py
files in the file system to indicate what it can import. PyScript doesn’t work this way, so you’ll need to specify which files you want to make available as importable modules.
To do this, you list the URLs you want to make available to PyScript in your application’s config file in a [files]
block, along with how you want to map them to PyScript’s emulated filesystem. For instance:
[files]
"/module.py" = "./libs/module.py"
"https://mydata.com/data.csv" = "./data.csv"
Whatever file is accessible from the URL on the left is made available to the Python interpreter’s emulated filesystem via the path on the right. In this case, the file you’d see if you browsed to /module.py
(e.g., http://localhost:8000/module.py
) is available to Python as libs.module
. Likewise, the file at the URL https://mydata.com/data.csv
is available in the emulated current working directory as data.csv
.
The in-browser terminal
Python users should be familiar with the REPL, the console interface to the Python runtime. In PyScript, you can embed into the browser a live terminal running the REPL, or just console output from your Python program.
To embed a terminal, use a script
tag that has terminal
as one of its attributes:
<script type="py" terminal>print("hello world")</script>
This opens a terminal and prints hello world
, but does not allow any interactivity. For interaction, you need to use the worker
attribute:
<script type="py" terminal worker>
name = input("What is your name? ")
print(f"Hello, {name}")
</script>
The worker
runs your program in a web worker, which is essentially a subprocess. Note that you cannot use web workers on an HTML file loaded locally; you must load it from a web server that provides certain headers. PyScript’s documentation explains how this works in detail.
Most of what’s possible in a regular console, like coloring and Unicode, should be supported in the PyScript terminal, too.
Interacting with the DOM and JavaScript
Because PyScript is based on browser technology, it has mechanisms for interacting with the DOM. For instance, if you wanted to get the value of an input box on a web page and use it in your Python code, you’d do this:
from pyscript import window, document
inputbox = document.querySelector("#my-input")
print("Value of input:", inputbox.value)
PyScript also includes a module called pydom
that allows dynamic creation of objects on the page:
from pyweb import pydom
new_div = pydom.create("div")
new_div.content = "Hello World"
This creates a new div
element on the page and populates it with text. Most of the other kinds of manipulations you can perform with the DOM in JavaScript—such as adding elements and changing attributes—can be done through the pydom
library.