PyScript: Python in the Browser
Tags: python, webdev, programming
Published: 2023-08-12 18:18:59

PyScript is tool that allows Python code to run directly in the browser via web assembly (Wasm). 

![WebAssemMeme](https://img.devrant.com/devrant/rant/r_718849_VMP6g.jpg)

This opens up opportunities to use Python for frontend web development and interact with JavaScript environments from Python. In this blog post, we'll explore what PyScript is, how to use it, and provide some code examples.

## What is PyScript?

PyScript is a framework that allows users to create rich Python applications in the browser using HTML’s interface and the power of Pyodide, WASM, and modern web technologies. The PyScript framework provides users at every experience level with access to an expressive, easy-to-learn programming language with countless applications. [It enables users to run Python code directly in their browser, giving anyone the ability to program without infrastructure barriers. You can add an interactive Python REPL directly to your website, share an interactive dashboard with a colleague as an HTML file, or create a client-side Python-powered web application.

Some of the key features of PyScript include:

- It allows users to write and run Python code in HTML.
- It allows users to build web applications using Python
- It allows sharing machine learning models in an HTML file so that others can run those models in their browsers
- It helps to avoid setup
- It allows using UI components such as buttons, containers, text boxes, etc

So in summary, PyScript allows writing frontend code with the Python programming language and interacting seamlessly with JavaScript environments, all running natively in the browser via WebAssembly.

## How do I use PyScript?

To use PyScript, you do not require any development environment other than a web browser (Chrome is recommended. Long Live 🔥🦊) and a text editor, even though using your IDE of choice might be convenient

That's it. There is no installation required. The PyScript assets served on pyscript.net which makes the process of getting up and running pretty easy. 



## Our first PyScript file

Using your favorite editor, create a new file called with the `html` extension (This ideally should be in the same directory as your support Python Script(s), JavaScript, and CSS files with the following content, and open the file in your web browser.

For the sake of brevity, let's assume the file you created is called `index.html`

```html
<html>
  <head>
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css"/>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
  </head>
  <body>
    <input type="color" id="color-picker">
    <div id="text" style="font-size: 48px">Hello World!</div>
    <py-script>
from js import document
from pyodide.ffi import create_proxy
color_picker = document.getElementById("color-picker")
text = document.getElementById("text")
def on_color_change(event):
	text.style.color = event.target.value
color_picker.addEventListener("change", create_proxy(on_color_change))
    </py-script>
  </body>
</html>
```

We start by linking PyScript and you you can do that in two ways:

1.  Download a copy of the latest version of PyScript, and add the required files to `head` in your HTML document:
2.  Add the CDN version of PyScript's JS and CSS files to `head` in your HTML document:

This script is an HTML file that uses PyScript to create a color picker and change the color of the text based on the selected color. The logic is present inside the `<py-script>` tag

```html
<html>
  <head>
      ...
  </head>
  <body>
    <input type="color" id="color-picker">
    <div id="text" style="font-size: 48px">Hello World!</div>
    <py-script>
from js import document
from pyodide.ffi import create_proxy
        
color_picker = document.getElementById("color-picker")
text = document.getElementById("text")
        
def on_color_change(event):
	text.style.color = event.target.value
        
color_picker.addEventListener("change", create_proxy(on_color_change))
    </py-script>
  </body>
</html>
```

When you open this file in a web browser, you will see an input element of type `color` that allows you to select a color, and a `div` element containing the text “Hello World!” in a large font size. 

The script uses PyScript to select the color picker and the `div` element using their IDs, and defines a function `on_color_change` that is called whenever the value of the color picker changes. 

In this function, the `style.color` property of the `div` element is set to the value of the color picker. 

The script also attaches an event listener to the color picker to call the `on_color_change` function whenever its value changes. As a result, when you select a color using the color picker, the color of the text will change accordingly.

If you take a closer look at the `addEventListener`, you'll notice a function called `create_proxy` that wraps the `on_color_change` function. 

This purpose of this function comes from the fact that Pyodide needs to briefly proxy the Python function `on_color_change` so the JS function `addEventListener` knows how to interact with it. But once `addEventListener` terminates, that proxy is no longer needed, it gets destroyed... and then when an event comes around to trigger your function, the proxy it should be using is gone. This will cause the script to return the following error

```
Uncaught Error: This borrowed proxy was automatically destroyed at the end of a function call. Try using create_proxy or create_once_callable.
The object was of type "function" and had repr
```

The two functions that are mentioned in the error create a [PyProxy](https://github.com/pyodide/pyodide/blob/b193fe1f5dfc1e7831114bc0eac06ca661bcf1a0/src/core/pyproxy.ts#L546)  (a JS object) of your Python object that you, the user, are supposed to manage the lifetime of, by calling [`PyProxy.destroy()`](https://github.com/pyodide/pyodide/blob/b193fe1f5dfc1e7831114bc0eac06ca661bcf1a0/src/core/pyproxy.ts#L617) on it when you're done with it. Or, if you use [`create_once_callable()`](https://github.com/pyodide/pyodide/blob/b193fe1f5dfc1e7831114bc0eac06ca661bcf1a0/src/core/pyproxy.c#L1311), the proxy will `destroy()` itself after the first time it's called.

For an object such as an event listener, destroying the proxy for the lifetime of your page may not be a desirable thing, so you can just leave it hanging around. It's worth noting that if you remove that event listener or the element attached to it, you should plan to track and destroy the PyProxy, to avoid taking up memory.

## Interacting with JavaScript

A major benefit of PyScript is being able to directly interact with JavaScript from Python code. This enables full integration with frontend frameworks and libraries.

PyScript provides a `js` module that exposes JavaScript functions and objects to Python. For example:

```python
<html>
  <head>
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css"/>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <script>
    name = "🦊";
    function sayMyName(name){
        console.log(name);
    }
	</script>
  </head>
  <body>
	<py-script>
from js import name, sayMyName

print(f"Hello {name}")
print("Saying your name in Javascript: " + str(sayMyName('Dev.to')))
	</py-script>
  </body>
</html>
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7r8iemg2lgzvtqms3om.png)

Here we define a Python function that calls out to JavaScript's `console.log()`. When compiled and run, it will print the passed name. It can also be seen in the console of the web browser.

We can also access global JavaScript objects. For example, to display a alert popup:

```python
import js

js.alert("Hello from Python!")
```

So PyScript provides full interoperability between Python and JavaScript for building hybrid applications.

## Async/Await Support

Another key feature of PyScript is built-in support for asyncio and async/await syntax. This enables writing asynchronous Python code that integrates cleanly with async-based JavaScript code and event loops.

For example, we can write an async function that performs a simulated async task:

```python
<html>
  <head>
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css"/>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
  </head>
  <body>
	<py-script>
import asyncio
import js
from pyodide.ffi import create_proxy

async def do_task():
  await asyncio.sleep(1)
  print("Task finished!")

async def main():
  await do_task()
  print("Done!")
  
js.setTimeout(create_proxy(main), 0)  
	</py-script>
  </body>
</html>
```

Here we define an async function `do_task()` that sleeps for 1 second, and call it from the main function `main()`. We schedule `main()` to run asynchronously after 0 seconds using JavaScript's `setTimeout()`.

When compiled and run, this will output the lines with a 1 second delay in between, demonstrating asyncio integration with the browser's event loop.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xh8qxyqdz60pvdgcdvi1.png)



## Conclusion

PyScript provides an intriguing new way to use Python for frontend web development by compiling it to WebAssembly. It maintains the core Python syntax and semantics while enabling seamless interoperability with JavaScript.

Some key advantages it offers include:

- Familiar Python syntax for writing frontend code
- Full asyncio/await support for asynchronous programming
- Interactive usage directly in browsers via debugging tools
- Performance comparable to JavaScript with WebAssembly compilation
- Reuse of Python skills, libraries and frameworks

PyScript is a powerful tool for building frontend apps with Python. I'm excited to see where the project goes and do intend to play around with it a little more