DEV Community

loading...
Cover image for Use Python in your browser client code together with JS. No server required, thanks to Web Assembly

Use Python in your browser client code together with JS. No server required, thanks to Web Assembly

rubenwap profile image Ruben Sanchez ・3 min read

Use case: Let't say that I love Python Pandas for data manipulation, but at the same time I need to use the manipulation results in a regular client-side webpage. The classic method I would do is to have a Python backend, do my calculations in there and then send the results to the front, via an API, server rendered templates or whatever.

I am going to present you an alternative based on Web Assembly and the Pyodide project.

Web Assembly is a standard (supported in all major browsers) that roughly speaking says "This is how executable binaries should look like and here is how they should interact with the host" (the host in this case being the browser). That opens the door to client side applications written in a wide variety of languages. Take a look at this repo with the list of available languages:

https://github.com/appcypher/awesome-wasm-langs

One of the fruits of this project has been Pyodide, which allows you to execute real Python code in the browser. Do not mistake this with other projects that allow you to write Python and then transpile it into Javascript so it can run in the browser. Here what is running is the actual Python code.

We can do a quick demo and then I walk you through what just happened:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        window.languagePluginUrl = 'https://pyodide-cdn2.iodide.io/v0.15.0/full/';
    </script>
    <script src="https://pyodide-cdn2.iodide.io/v0.15.0/full/pyodide.js"></script>
</head>
<body>
    Pass JS data to Python pandas (fully in-browser) and returns results back to JS
    <script type="text/javascript">
        const data = JSON.stringify([{ "name": "Ruben", "surname": "Sanchez" }, 
        { "name": "Michael", "surname": "Johnson" }, 
        { "name": "John", "surname": "Smith" }])
        languagePluginLoader.then(function () {
            pyodide.loadPackage(['pandas']).then(() => {
                pyodide.runPython(`
                import pandas as pd
                df = pd.DataFrame(${data})
                count = df["name"].count()
               `)
                console.log(pyodide.pyimport('count'))
            });
        });
    </script>
</body>

Save that snippet in a new html file or run it from this CodePen https://codepen.io/rubenwap/pen/yLYZORW Note that the results you are looking for will be in the console tab! last console.log in the code should print 3, and that is the value we have taken from Python.

Overall, We have done three nice things:

 1) We have defined JS data to be used inside our Python script

Your starting point for this mix'n'match session can be regular Javascript data that you have obtained in any way you like:

 const data = JSON.stringify([{ "name": "Ruben", "surname": "Sanchez" }, 
        { "name": "Michael", "surname": "Johnson" }, 
        { "name": "John", "surname": "Smith" }])

2) We have executed actual Python code in the browser, including Pandas!

Don't be fooled by the ugly way in which you need to specify your Python code (as string inside the runPython method). You could write an actual nice .py file and then import the text contents via Ajax, so it would look nicer.

Note that here, loadPackage is acting somehow like an installation of that package. If you try to import pandas without having loaded it first, it won't work.


 pyodide.loadPackage(['pandas']).then(() => {
                pyodide.runPython(`
                import pandas as pd
                df = pd.DataFrame(${data})
                count = df["name"].count()
               `)

3) We have taken the results of our Python scripts and sent them back to Javascript

    console.log(pyodide.pyimport('count'))

I think this is pretty cool, the fact that you can run something in a totally different language and have it available in Javascript without having to go back and forth with API requests.

If you found this interesting, don't forget to explore their docs:
https://pyodide.readthedocs.io/en/latest/#

And generally, WASM docs, specially if you code in any of the other compatible languages:
https://webassembly.org/getting-started/developers-guide/

Discussion

pic
Editor guide
Collapse
jackryan1989 profile image
Jack Ryan

Hey Reuben, this is super exciting! Thanks for showcasing this. For someone who is a total newbie to WASM (and web development generally) can you point out the bits in your snippet that are WASM functions/methods? For example is the window.languagePluginUrl a WASM method?

Collapse
rubenwap profile image
Ruben Sanchez Author

Thanks Jack! I am a newbie myself in WASM, so I hope I am not getting this wrong:

The methods and functions that you see in the snippet are all Javascript. The .wasm module which contains the Python distribution used for this (Pyodide) is also loaded via Javascript, but you don't see it here because the loading takes place in the imported pyodide.js file

This Pyodide project not only compiles Python into Web Assembly, but it also provides a Javascript API so you can interact with it via the browser, so all what you see here are functions related to the API. You might be interested in this, which contains the JS API explanation: pyodide.readthedocs.io/en/latest/a...

Collapse
pepperwood profile image
Kathryn DiPippo

Wow! That's awesome that the python snippet is baked into JS script itself (without needing to run the code through a wasm compiler). I'm super stoked to try this out.