An Introduction to PyScript

0
83
PyScript

An open source framework introduced by Anaconda, PyScript is built to run Python from a browser. Though it still has performance issues and a few bugs, it is important to get familiar with it as it promises to be popular in the future.

We are aware that JavaScript client-side coding can be used for server-side coding. Let’s now discuss the reverse — using a server-side language from the browser with the help of Python.

But why would one want to execute server-side programs in the browser? The answer is: for enhanced performance, features and convenience. In certain use cases running a code in C is much faster than using JavaScript — for example, for generating quick reports, dealing with heavy prime numbers, image processing or computer vision on-the-fly using the web page, and so on.

PyScript is a framework built to run Python from a browser; in simple terms, it can see Python code as part of HTML page content. PyScript allows the use of CPython based packages like Pandas and NumPy. It provides bi-directional communication between Python and JavaScript objects and namespaces.

Note that this is not something new in the history of Python. There are already modules that support browser side coding such as Brython, Skulpt, PyJS, and a few more. Everything in this world has its own merits and demerits. Brython does support packages if they are written entirely in Python. However, programs that use extensions written in C are not supported. For instance, NumPy, MatPlotLib, and Pandas cannot work with Brython. Skulpt, on the other hand, does not support a few Python features such as import of additional Python modules that are not available by default.

PyScript has been launched by Anaconda and was officially announced at PyCon US 2022.

How PyScript works

As rightly stated by Anaconda, its Pyodide, Emscripten and WebAssembly (WASM) that create the magic of PyScript. So let’s look at each of these and see how they help in building PyScript.

WASM: As we all know, computers understand only binaries, and the same goes for JavaScript as well. A JavaScript engine is needed to read the code and convert it into binary instructions.

WebAssembly or WASM is a language for the web, which is used to code in low level binary format. It can run in modern web browsers.

When WebAssembly was originally developed in 2017 by Mozilla, web browsers only supported JavaScript. It quickly became the official World Wide Web Consortium (W3C) standard by 2019. WASM employs a human readable .wat text format language, which gets converted to a binary .wasm format that browsers can run on-the-fly.

This allows us to write code in any language such as C, C++, Rust, etc; compile it to WebAssembly and then run in a web browser just like JavaScript.

This is the fundamental technology that makes it possible to write websites in Python.

Emscripten: Emscripten, an open source compiler toolchain, enables developers to develop frontends from any programming language.

It compiles any portable C/C++ codebase into WebAssembly, using the LLVM (low level virtual machine) compiler. The LLVM project started in 2000 at the University of Illinois at Urbana–Champaign, under the direction of Vikram Adve (IIT Mumbai alumnus) and Chris Lattner.

The Emscripten output can be used to run on the web, in Node.js or in WASM runtimes.

It focuses on speed, size, and the Web platform.

Pyodide: Pyodide is the foundation of PyScript. It is a Python distribution (a CPython version) for the browser and Node.js, based on WebAssembly.

This Mozilla project allows for wide support of Python packages and the ability to run Python in the browser. PyScript uses Pyodide to execute code located in py-script elements directly in the browser.

Using Pyodide, we can install and run Python packages in the browser with micropip. Micropip is used to install the given package and all its dependencies. This only works for packages that are either pure Python or for packages with C extensions that are built in Pyodide.

Many packages with C extensions have also been ported for use with Pyodide. These include many general-purpose packages like RegEx, PyYAML, lxml and Python packages, including NumPy, Pandas, SciPy, MatPlotLib, and scikit-learn.

We can now connect the dots as to how Python and CPython code can be executed from the web. Figure 1 shows the architecture of PyScript.

Figure 1: PyScript architecture (Image credits: https://www.anaconda.com/blog/pyscript-python-in-the-browser)

Core components of PyScript

Here are a few core components defined in the PyScript documentation. We will explore each with an example. The examples are taken from PyScript’s GitHub example repo and real Python link.

PyScript doesn’t need to be installed; we only have to import pyscript.js and pyscript.css. If there is no internet access it can be downloaded and used directly in the HTML files.

Python in the browser

Enable drop-in content, external file hosting, and application hosting without the reliance on server-side configuration. Let’s write a simple ‘Hello World’ Python code in the browser.

Python code is enclosed in the custom py-script tag. Like JavaScript, one can have many py-script tags.

Here is sample code with py-script:

<!DOCTYPE html>
<html>
<head>
<link rel=”stylesheet” href=”https://pyscript.net/alpha/pyscript.css” />
<script defer src=”https://pyscript.net/unstable/pyscript.js”></script>
</head>
<body>
<py-script>
print(“Hello World From Python”)
</py-script>
</body>
</html>

The output of the code is shown in Figure 2.

Output of py-script tag
Figure 2: Output of py-script tag

Python ecosystem and environment management

Run many popular packages of Python and the scientific stack (such as NumPy, Pandas, scikit-learn, and more). Allow users to define what packages and files to include for the page code to run.

  • The py-env tag specifies third-party packages need to be installed; this acts like requirements.txt. Modules such as Bokeh and NumPy have been used in the code given below.
  • The standard module can be directly imported in the py-script tag; we have imported JSON.
  • User defined modules need to be specified under the py-env tag, mentioning the path, and further imported in the py-script tag. Here we have specified the utils file under the py-env tag and imported it in the py-script tag.

In the py-script tag we have saved output in the ‘out’ id, and later used it in div. We can source the Python file similarly.

py-script src=”./todo.py”> </py-script>

Here is the code for py-env:

<<html><head>
<title>Bokeh Example</title>
<meta charset=”iso-8859-1”>
<link rel=”icon” type=”image/x-icon” href=”./favicon.png”>
<script type=”text/javascript” src=”https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js”></script>
<script type=”text/javascript” src=”https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.2.min.js”></script>
<script type=”text/javascript” src=”https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js”></script>
<script type=”text/javascript” src=”https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js”></script>
<script type=”text/javascript” src=”https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js”></script>
<script type=”text/javascript”>
Bokeh.set_log_level(“info”);
</script>
<link rel=”stylesheet” href=”https://pyscript.net/alpha/pyscript.css” />
<script defer src=”https://pyscript.net/alpha/pyscript.js”></script>
</head>
<body>
<py-env>
- bokeh
- numpy
- paths:
- ./utils.py
</py-env>
<h1>Bokeh Example</h1>

<py-script output=”out”>
from utils import now
print(“Current time: {}”.format(now()))
</py-script>

<div id=”out”></div>
<div id=”myplot”></div>
<py-script id=”main”>
import json
import pyodide
from js import Bokeh, console, JSON
from bokeh.embed import json_item
from bokeh.plotting import figure
from bokeh.resources import CDN
# create a new plot with default tools, using figure
p = figure(plot_width=400, plot_height=400)
# add a circle renderer with x and y coordinates, size, color, and alpha
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color=”navy”, fill_color=”orange”, fill_alpha=0.5)

p_json = json.dumps(json_item(p, “myplot”))
Bokeh.embed.embed_item(JSON.parse(p_json))
</py-script>
</body>

</html>

The output for this is shown in Figure 3.

Bokeh example
Figure 3: Bokeh example

Python with JavaScript

There is bi-directional communication between Python and JavaScript objects and namespaces. PyScript uses Pyodide’s proxy objects to interact with JavaScript objects as WebAssembly doesn’t currently allow direct interaction with the document object model (DOM), which would typically give you access to HTML’s underlying elements.

In the example given below we are dynamically creating list objects by calling a Python function and clicking the button.

The source code is:

<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8”>
<meta name=”viewport” content=”width=device-width, initial-scale=1”>
<title>DOM API in PyScript</title>
<link rel=”stylesheet” href=”https://pyscript.net/alpha/pyscript.css” />
<script defer src=”https://pyscript.net/alpha/pyscript.js”>
</script>
</head>
<body>

<div id=”shopping-list”>
<p>Shopping List</p>

<ul>
<li>milk</li>
<li>eggs</li>
<li>bread</li>
</ul>
<input id=”new-item” type=”text” placeholder=”Add new item”>

<button>Add</button>
</div>
<py-script>

from pyodide import create_proxy
def on_add_click(event):
input_new_item = document.querySelector(“#new-item”)

if input_new_item.value:
child = document.createElement(“li”)
child.innerText = input_new_item.value


parent = document.querySelector(“#shopping-list ul”)
parent.appendChild(child)
input_new_item.value = “”

button = document.querySelector(“button”)
button.addEventListener(“click”, create_proxy(on_add_click))
</py-script>
</body>
</html>

Figure 4 shows the output for this example code.

Dynamic list creation
Figure 4: Dynamic list creation

Flexible framework

A flexible framework can be leveraged to create and share new pluggable and extensible components directly in Python. The <py-repl> tag creates an REPL component that is rendered to the page as a code editor, allowing you to write executable code inline.

The code given here depicts the py-repl tag:

<html>
<head>
<link rel=”stylesheet” href=”https://pyscript.net/alpha/pyscript.css” />
<script defer src=”https://pyscript.net/alpha/pyscript.js”></script>
</head>
<py-repl></py-repl>
</html>

Figure 5 shows the output for the above code.

py-repl tag
Figure 5: py-repl tag

Visual application development

Here, readily available curated UI components can be used, such as buttons, containers, text boxes, and more. The following tags can be used to add visual attributes to your HTML page.

  • <py-inputbox>: Adds an input box that can be used to prompt users to enter input values.
  • <py-box>: Creates a container object that can be used to host one or more visual components that define how elements of <py-box> should align and show on the page.
  • <py-button>: Adds a button to which authors can add labels, and event handlers for actions on the button, such as on_focus or on_click.
  • <py-title>: Adds a static text title component that styles the text inside the tag as a page title.
<html>
<head>
<link rel=”stylesheet” href=”https://pyscript.net/alpha/pyscript.css” />
<script defer src=”https://pyscript.net/alpha/pyscript.js”></script>
</head>

<py-title>PyScript Playground</py-title>
<body>
<py-button label=”Click me” styles=”btn big”>
def on_click(event):
print(event)
</py-button>


<br>
<br>
<py-inputbox>
def on_keypress(event):
print(event)
</py-inputbox>
<br>
<br>
<py-box widths=”2/3;1/6;1/6”>
<div>Wide Column</div>

<div>Narrow Column</div>
<div>Narrow Column</div>
</py-box>
</body>
</html>

JavaScript vs PyScript

So, do we have a new language in PyScript that will replace JavaScript? That’s a million-dollar question.

JavaScript is a high-level programming language and one of the core technologies of the World Wide Web. And it is used as a client-side programming language by 97.8 per cent of all websites.

However, Python is not far behind in the popularity race and is one of the most liked languages in the world. So definitely, but not immediately, there will be a user space shift from JavaScript to PyScript. This is specifically in the data science world, as it helps build pages with plots easily. Nonetheless, performance is where PyScript lags far behind, but this is just the beginning and I am confident the language will evolve. 

PyScript playground
Figure 6: PyScript playground

I am sure we will continue to use loads of JavaScript. It’s not easy or necessary to replace it yet. Having said that, PyScript or Python and other language-based browser scripting will make their own user space too. PyScript is bound to grow using WASM technology.

In the end, let’s just recap a few important tags:

  • py-script tag is where you write your Python code that gets executed within the web page.
  • py-env tag defines the Python packages needed to run your Python code.
  • py-repl tag creates a REPL (read-eval-print loop) component that evaluates the code users enter and displays the results.
  • py-config tag sets and configures general metadata about your PyScript application in YAML format.

In my opinion, PyScript is good for proof-of-concepts, but I would be a little hesitant to use it for business for some more time. Performance and a few bugs are two big reasons at present. Nonetheless, this is a very good attempt based on a vision of the future.

LEAVE A REPLY

Please enter your comment!
Please enter your name here