An Introductory Guide to Brython - Stack Abuse

An Introductory Guide to Brython

Introduction

When developing web applications - we commonly use several technologies, and languages. A back-end can easily be built in Java (Spring Boot), Python (Django or Flask), or JavaScript (Node.js), though the front-end is more commonly done in JavaScript (React, Angular, etc). Sometimes, we even take the hybrid approach of having server-side rendered pages, with final touchups done in front-end frameworks such as React.

Through the years, given its prevalence on the web - the JavaScript community expanded the original functionality to enable JavaScript-powered back-ends, including front-ends. The most common way to code web applications in JavaScript is to use the MEAN stack. A MongoDB database, Node.js with Express.js for the back-end, and Angular (or more recently, React) for the front-end.

But what if you really prefer to develop your apps using Python? While being strictly focused on one programming language isn't advisable - languages are tools, and being fixated on one tool makes you less flexible - there is still space for single-language applications.

Brython might be the solution! It's a JavaScript library that enables you to run Python code inside your browser.

You probably guessed, Brython stands for Browser Python

As its name suggests, Brython's main goal is to replace JavaScript and push Python as the primary scripting language for web browsers, for your application:

<html>
    <head>
        <script src="/brython.js"></script>
    </head>
    <body onload="brython()">
        <script type="text/python">
            import browser
            browser.document <= "Hello world!"
         </script>
    </body>
</html>

The <script> which usually doesn't support the text/python type can interpret the Python code we've wrote. Here, we've printed a Hello World message to the browser.document, which is analogous to JavaScript's document.

In this Introductory Guide to Brython - we'll take a look at how to install Brython, how to initialize a Brython project, how to style pages, as well as compare it to some alternatives.

How to Install Brython

Taking Advantage of Content Delivery Networks

Probably the most convenient way of installing Brython is, in fact, not to install it at all. If you don't need to install it locally to your PC, and only need it to load on a static web page to add some dynamic functionality to the page, you should consider simply importing an external resource.

The idea is to load the brython.js library in the <head> section of the HTML page. This way, the client will download the library at the same time as the HTML page loads on their PC.

To achieve this behavior we will load our library from some of the CDNs (Content Delivery Networks) that are hosting the latest stable version of Brython online.

A Content Delivery Network is, in basic terms, a group of distributed servers that are hosting some data (code, video content, images...). These types of networks are highly reliable and have almost no downtime. That makes them ideal for hosting code libraries.

There are several CDNs available to choose from, though, three popular ones are:

<!-- Option 1 : jsDelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/brython.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/brython_stdlib.js"></script>

<!-- Option 2: CloudFlare CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.1/brython.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.1/brython_stdlib.min.js"></script>

<!-- Option 3: GitHub as the CDN -->
<!-- Choose this option if you want to use the latest developement version -->
<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython.js"></script>
<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython_stdlib.js"></script>

Installing Brython via Pip

If you want more flexibility with Brython, you can install it locally:

$ pip3 install brython

pip will download and install the package on your local machine without breaking a sweat. Let's verify that Brython has been successfully installed:

$ pip show brython 

This prints out the version, as well as some basic information on the Brython package:

Name: brython
Version: 3.9.2
Summary: Brython is an implementation of Python 3 running in the browser
Home-page: http://brython.info
Author: Pierre Quentel
Author-email: [email protected]
License: BSD

How to Initialize a Brython Project

After installing Brython, the obvious next step is to create a simple project to test its capabilities. To create the project, create a new folder and move into it:

$ mkdir brython-project
$ cd brython-project

Now you can run the following command to initialize a Brython project:

$ brython-cli --install

This creates and initializes a Brython project, including the starting project directory and file hierarchy:

brython-project
    | brython.js
    | brython_stdlib.js
    | demo.html
    | index.html
    | README.md
    | unicode.txt

First, let's explain what all these files are for:

  • brython.js - The Brython core engine, it includes the most commonly used modules such as browser, browser.html, javascript... This file is included in the HTML page using the <script> tag if we choose not to install Brython locally.
  • brython_stdlib.js - Consists of all the packages and modules from the Python standard library that are supported by Brython.
  • demo.html - A simple HTML page running Brython, showcases some interesting use-cases and examples of how we can utilize Brython to modify static HTML pages.
  • index.html - A simple Hello World HTML page.

It is possible to open demo.html using just a simple web browser, but this approach comes with its limitations, so it's recommended that you start a localhost server first.

If you don't already have the http module installed, you can also install it via pip:

$ pip3 install http

Once installed, we can spin up the server:

$ python3 -m http.server

Now, you should have the localhost started on a (default) port 8000, and you should be able to access the demo.html page by navigating to http://localhost:8000/demo.html (or http://0.0.0.0:8000/demo.html) in the address bar of your web browser of choice.

If the port 8000 is currently used by some other process, you'll have to define another port to use (e.g. 8080):

$ python3 -m http.server 8080

To create a new HTML page that would be able to run Python, you just need to import the brython.js and brython_stdlib.js files in the head section of the file. Then you can move on to writing Python in the HTML file itself:

<script src="brython.js"></script>
<script src="brython_stdlib.js.js"></script>

How Brython Works

Brython enables us to write and run Python code in the browser by transpiling it to JavaScript. This code will be able to run in all modern browsers supporting JavaScript, because Brython purposely avoids generating JavaScript with new, unsupported syntax.

You can think of transpiling as a subset of compiling.

The process of compilation usually converts the source code written in some high-level programming language (e.g. C) into some lower-level language (e.g. machine code).

On the other side, transpilation is a process of converting one high-level language into another high-level language (e.g. Python to JavaScript).

Transpilation in Brython happens at the same time that the HTML page is loaded. Here, we call the brython() function in the body tag of the HTML document:

<body onload="brython()">

The brython() function performs transpilation of the Python code that is written in the <script type="text/python"> tags of the HTML document. All Python code must be surrounded with the <script type="text/python"> tag:

<script type="text/python">
    <!-- Python code -->
</script>

Alternatively, we can include external Python code by using the following command to load it into the HTML document:

<script type="text/python" src="test.py"></script>

All modern web browsers support JavaScript as the main scripting language but don't have support for Python. Therefore, all Python code needs to be translated to JavaScript and then run in the time it takes to load the HTML page.

First, the brython() function searches for all the Python code in the HTML page by inspecting all scripts that have a type of text/python and then translates all that code to JavaScript:

The result of this translation is a simple string representation of the JavaScript code. That string must be run as JavaScript code in a browser.

Brython uses the JavaScript eval() function to run all of the translated code. Alternatively, it can make use of JavaScript command new Function(function_name, source)(module) to run the code on some browsers.

This isn't the preferred way of running JavaScript. Using eval() can be dangerous because it could expose the application to potentially malicious third-party code. Also, eval() is pretty slow compared to the alternatives.

Getting Started with AWS in Node.js

Build the foundation you'll need to provision, deploy, and run Node.js applications in the AWS cloud. Learn Lambda, EC2, S3, SQS, and more!

If the Python code is loaded into the HTML document via <script type="text/python" src="url">, Brython performs an Ajax call to get the content of the loaded file. That code is translated to JavaScript and executed the same way as described above.

Working with Brython - Examples

Now, let's go over a few simple examples so that you get an idea of how Brython works and what it is capable of:

Hello World

<html>
    <head>
        <script src="/brython.js"></script>
    </head>
    <body onload="brython()">
        <script type="text/python">
            import browser
            browser.document <= "Hello world!"
        </script>
    </body>
</html>

We will focus on the Python code between the <script type="text/python"></script> tags:

  • import browser loads the browser package into the script. It is the package that groups all of the Brython-specific names and modules, mainly used to represent DOM elements and events used in JavaScript.
  • browser.document is an object that represents the currently shown HTML document.
  • browser.document <= "Hello world!" - we are using <= notation instead of =. The document "receives" the new element containing the string Hello world!. An alternate approach is to use the following syntax: browser.document.attach("Hello world!").

On the client-side, once this code is rendered - it results in:

<html>
    <head>
        <script src="/brython.js"></script>
    </head>
    <body onload="brython()">
        Hello world!
    </body>
</html>

Adding Elements and Attributes

Let's modify the previous example and add some paragraphs and text formatting to it. The browser interface provide us with the html module, which exposes HTML tags which we can use to dynamically create a HTML structure from Python code. The syntax to create an object is:

browser.html.TAG("content", [attributes])

Which outputs:

<TAG [attributes]>content</TAG>
  • browser.html.H2("Hello world!") wraps the Hello world string with the <h2> tag.
  • browser.html.A("link", href="stackabuse.com") creates an <a href="stackabuse.com"> tag.

Nesting is also possible with this kind of syntax, simply by including an html.element within another element. Let's add a few elements to our page:

<html>
    <head>
        <script src="/brython.js"></script>
    </head>
    <body onload="brython()">
        <script type="text/python">
            import browser
            
            title = browser.html.H2("Hello world!")

            bold = browser.html.B("bold text")
            url = browser.html.A("link", href="stackabuse.com")
            paragraph = browser.html.P("This is a paragraph. This is " + bold + ", and this is a " + url)

            browser.document <= title
            browser.document <= paragraph         
        </script>
    </body>
</html>

Alternatively, instead of creating an object with arguments like url = browser.html.A("link", href="stackabuse.com"), you could create it without any arguments and build it up:

# Creating an <a></a> tag
url = browser.html.A()

# Adding content between created tags
# <a>Url Text</a>
url <= "Url Text"
# Adding href attribute
# <a href="stackabuse.com">Url Text</a>
url.href = "stackabuse.com"

When we're finished with the Python code and open the page in a browser - the generated HTML page should look like this:

<html>
    <head>
        <script src="/brython.js"></script>
    </head>
    <body onload="brython()">
        <h2>Hello world!</h2>
        <p>
            This is a paragraph. This is <b>bold text</b>, and this is a 
            <a href="stackabuse.com">link</a>.
        </p>
    </body>
</html>

We've got a <p> element, inside of which we've used a <b> and <a> element, constructed beforehand.

Creating Tables with Brython

Tables can be created with much the same logic we've been applying so far:

table = browser.html.TABLE()

Now, let's create several rows with some mock data and add them to the table:

# Creating the row
row = browser.html.TR()
# Adding header cells
row <= browser.html.TH("Header1")
row <= browser.html.TH("Header2")
# Appending the row to the table
table <= row

# Adding a first row
row = browser.html.TR()
row <= browser.html.TD("Data 1")
row <= browser.html.TD("Data 2")
table <= row

Finally, we opt to show the table in the bank <div id="table-zone"> element created on the HTML page:

tableZone = browser.document["table-zone"] 
tableZone <= table

This results in an HTML table on our page:

<div id="table-zone">
    <table>
        <thead>
            <tr>
              <th>Header 1</th>
              <th>Header 2</th>
            </tr>
        </thead>
        <tbody>
            <tr>
              <td>Data 1</td>
              <td>Data 2</td>
            </tr>
        </tbody>
    </table>
</div>

Adding Styles to the Existing Elements

Let's add some styling to the <div id="table-zone"> and table elements:

tableZone.style = {
            "background-color": "#dedfdd",
            "width": "50%",
            "min-height": "100px",
            "margin": "auto"
            }
            
table.style = {
            "border": "1px solid #333",
            "margin": "auto"
            }

This will result modified HTML tags with style attribute added:

<div id="table-zone" style="background-color: rgb(222, 223, 221); width: 50%; min-height: 100px; margin: auto;">
    
<table style="border: 1px solid rgb(51, 51, 51); margin: auto;">

Binding Actions and Reading Content from Elements

Web pages are not only for displaying data - they're also for capturing data. Forms are one of the most fundamental way we can prompt users to send data. Let's make a form in Brython, using the FORM() function, along with other HTML elements such as INPUT() and LABEL():

# Create a <div id="form-div"> element as a container for a new form
formDiv = browser.html.DIV(id="form-div")
# Create a <form> element 
form = browser.html.FORM()

# Create the <input type="text"> field wit the label and add it to the form
input = browser.html.INPUT()
input.type = "text"
input.id = "input-name"
# Add label and to the form
form <= browser.html.LABEL("Enter your name: ") + input

# Create the submit button and add it to the form
button = browser.html.INPUT()
button.type = "button"
button.value = "Submit"
button.id = "submit-button"
form <= button

# Add form to the container <div> element
formDiv <= form
# Add the <h4 id="form-response"> to show the value from the form
formDiv <= browser.html.H4("Your name is: ", id="form-response")
# Display the div element containing form on the page
browser.document <= formDiv

A form that doesn't do anything isn't very useful. We can use custom functions within Brython too. Let's make a Python function that's called upon clicking the submit button. It will alert the user that the button has been clicked and update the value of the <h4 id="form-response"> element:

def onSubmit(ev):
    # Get the value of the <input id="input-name"> field
    name = browser.document["input-name"].value
    # Append the stored value to the content in the <h4 id="form-response"> tag
    browser.document["form-response"] <= name
    # Alert the user that the button has been clicked
    browser.alert("The Submit Button is Clicked")       

Finally, we bound the click event of the submit-button with the created onSubmit() function, so that we have the desired behavior on the button click:

browser.document["submit-button"].bind("click", onSubmit)

How Does Brython Compare to Alternatives

There are several other solutions for running Python code in the web browser besides Brython, so which one should you choose?

System Time of compilation Running mechanism
BRYTHON On page load Transpiles Python to JavaScript
Transcrypt Ahead-of-time Transpiles Python to JavaScript
Batavia Ahead-of-time Python runtime in a browser
Skulpt After page load Transpiles Python to JavaScript
PyPy.js After page load Python runtime in a browser
Pyodide After page load Python runtime in a browser

Some of them tend to completely replace JavaScript, and some just create a useful Python environment for web browsers, as a possible alternative to JavaScript. By contrast, some of the solutions are transpiling Python code to JavaScript, like Brython, Skulpt and Transcrypt.

As far as time of compilation is concerned, it's performed either before, after, or at the time of loading an HTML document.

When benchmarking the speed of Python code execution in the browser, Brython is generally on the faster end of the spectrum. It tends to make a compromise between fast execution of the solutions that compile (transpile) Python to JavaScript ahead-of-time, and large files containing translated code that must be (down)loaded by the client to run the scripts in the "ahead-of-time" approach.

It seems that Brython is very close to that sweet spot.

Benchmarks obviously can't always be representative of real-world situations, as results may vary based on the code executed, but they can give a pretty good comparison between the performance of different solutions.

Note: By definition, Brython will be always slower than just using JavaScript for the same code. This is because of the added step of transpilation, which is never quite 0ms, after which JavaScript code is run.

Conclusion

If you are looking for an alternative to JavaScript as a scripting language for the web, and don't care about performance too much, then Brython could be a pretty good solution.

Its balanced approach to the inevitable tradeoff between execution speed and the excess memory usage required to load the library, makes it one of the best-performing solutions for running Python in the browser.

On the other hand, Brython doesn't have a huge community, and isn't widely accepted or used. Learning resources are very limited and you will be limited to mainly official documentation without many real-world large-scale projects to look to for guidance.

Ultimately, the main question is whether it is worth replacing JavaScript at all. Even small-scale Brython projects can be up to 2 times slower to execute compared to exactly the same projects written in JavaScript. Unlike Brython, JavaScript has a huge developer community, tons of resources, and real-world projects showing its full potential.

Let's not forget all of the JavaScript frameworks, they are the backbone of JavaScript's popularity. Without their help, JavaScript would be just another scripting language that provides us with the ability to dynamically change the content of static HTML pages. For example, imagine coding complex server-side logic in pure JavaScript. Even if that would be feasible, it certainly wouldn't be a very pleasant experience.

Unfortunately, Brython has no frameworks developed for it, so you are restricted to pure Python code, which isn't reasonable for anything other than simple use cases. You likely won't be able to create some complex one-page web application using Brython, as you could using JavaScript and Angular. Brython's a great tool for developers who want to use only Python for both server-side and client-side programming, but it is likely a long way off from replacing JavaScript.

Last Updated: June 15th, 2021

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Want a remote job?

    Prepping for an interview?

    • Improve your skills by solving one coding problem every day
    • Get the solutions the next morning via email
    • Practice on actual problems asked by top companies, like:
     
     
     

    Getting Started with AWS in Node.js

    Build the foundation you'll need to provision, deploy, and run Node.js applications in the AWS cloud. Learn Lambda, EC2, S3, SQS, and more!

    © 2013-2021 Stack Abuse. All rights reserved.