You are currently viewing the new Anvil Editor Docs.
Switch to the Classic Editor Docs
You are currently viewing the Classic Editor Docs.
Switch to the new Anvil Editor Docs

Python in the Browser

The user interface of an Anvil app is programmed in Python. Anvil compiles that Python into Javascript, and when someone visits your app the client-side Python code runs in their web browser.

Client-side code interacts with your user interface by manipulating the Python objects that represent components. When components raise events, they call functions in your client-side code.

def button_1_click(self, **event_args):
  """This method is called when the button is clicked"""
  alert(f"Logged in as: {anvil.users.get_user()['email']}")

Client-side code interacts with server-side code by calling server functions.

return_value = anvil.server.call('my_server_function', my_arg=[1, 2, 3])

What kind of Python?

Your Form code, which runs in the browser, runs in approximately Python 3.7. We say “approximately”, because in fact your Python code is being compiled to Javascript so it can run right in the user’s browser. The web browser is a constrained environment, so there are a couple of things missing (e.g. you can’t open a file in the browser!). That said, most things should work. Some older apps may use Python 2 for client code - see Upgrading to Python 3 for details.

If you need to use a module or language feature that is not available in the browser, don’t worry – you can make a function call to a Server Module which runs in a full Python environment. You can also make function calls to and from your own Python environments using the Uplink.

Our client-side Python support uses the open source Skulpt Python-to-Javascript compiler. We believe strongly in giving back to open source - if you want to join us, visit us on GitHub.

Everything is an object

Forms and components are represented in code as Python objects. You can instantiate Forms and components like any Python class:

label_instance = Label(text='Hello, World!')

from .Form1 import Form1
form1_instance = Form1(param='an_argument')

These Python objects have attributes and methods that you use to control the UI:

# Change the text on the Label
label_instance.text = "Hello again, World!"

# Get a list of all the components in form1_instance
components_on_form1 = [x for x in form1_instance.get_components()]

When displayed on the page, all Forms and components exist in a heirarchy with the top-level Form at the top. To add your Form and component instances into your app, you must add them to a Form or component in the hierarchy:

# Create a Label in code
label_instance = Label(text='Hello, World!')

# Add the Label to the top-level Form
top_level_form = get_open_form()
top_level_form.add_component(label_instance)

When your app starts

When a user opens your app in their browser, what happens first depends on which Form or Module is set to run at startup – that’s the one marked with the Lightning bolt icon. You can change your startup code in the App Browser.

If your app runs a Form on startup: Anvil will instantiate and then display that Form class, as if you had called the open_form() function with the name of that Form.

If your app runs a Module on startup: Anvil will load and run the specified Module as a Python script. Your code can then call open_form() to display its user interface.

Storing data in memory

Since the client-side Python runs in the user’s web browser, you can store data in the browser’s memory by creating variables in Python.

In the following example, we store a list of active filters as an attribute of the Form1 class. When the Form loads, the list of filters is empty. The Form contains a series of CheckBoxes, and when one is checked, we add its text to the list of active filters.

class Form1():
  def __init__(self, **properties):
    # No filters are active on startup.
    self.active_filters = []

  def check_box_change(self, **event_args):
    # When a CheckBox is changed, set the active filter accordingly
    check_box = event_args['sender']
    if check_box.checked:
      self.active_filters.append(check_box.text)
    else:
      self.active_filters.remove(check_box.text)

You can also create global variables that can be used by all your Forms.

This data persists as long as the app is open in the user’s browser tab. If you want to store data to be retrieved when the user re-vists your app, you can use Anvil’s built-in database, or use Sessions and Cookies.

The anvil module

API Docs

The anvil module is Anvil’s Python API.

Most of the Anvil-specific Python code in this documentation uses classes and functions from anvil. In particular, all the component classes are in the anvil module. Anvil’s core server-side functionality is in anvil.server (along with client-side functionality that relates to the server, such as anvil.server.call). Other features of Anvil have their own namespaces within anvil, such as anvil.email, anvil.users and anvil.http.

The anvil module is imported by default in Forms, and must be referred to explicitly in Server Modules (when you add a Service, the relevant imports are automatically written for you).

A complete listing of classes and functions in the anvil module is available in the API Reference.

Unit tests for client side code

A partial implementation of the Python ‘unittest’ library is available in the client side Python that runs in the browser.

On the server side, both ‘unittest’ and ‘pytest’ are available.

Limits to Python in the browser

Anvil compiles client-side Python into Javascript using the Skulpt compiler. This compiler supports a subset of Python 3.7. Some standard libraries are missing or incomplete – particularly libraries such as file manipulation that do not make much sense in a web browser. (Some older apps may be using Python 2.7 Skulpt. See our upgrade guide.)

If you want to use a library that is not available in client code, write a server function that uses that library, then call it from the client.

Blocking Code

Python code is blocking if the execution must wait for a process to complete before continuing. If you use time.sleep() or anvil.server.call() then you have written blocking code

If you receive a SuspensionError, it means the Javascript compiler does not support blocking code at that location.

The following are considered blocking in Python:

You’ll find that blocking code is supported almost everywhere in client-side Python. You should rarely need to think about it. However, there are certain places where blocking code is not supported.

Python Dunders

Overriding certain Python dunders (or magic methods) may result in a SuspensionError. For example, you may want to use blocking code in a custom __iter__ that invokes lazy loading of data using anvil.server.call(). Unfortunately, client-side Python does not support blocking code when creating an iterator object. __next__, however, does support blocking code. Lazy loading can therefore happen in the first call to __next__.

Common dunders that do not support blocking code:

  • __iter__
  • __repr__
  • __str__
  • __bool__
  • Number dunders like __add__, __mul__
  • Comparison dunders like __eq__, __gt__

Common dunders that do support blocking code:

  • __getattr__, __setattr__
  • __getitem__, __setitem__
  • __next__
  • __dir__
  • __init__, __new__
  • __len__
  • __contains__

Do you still have questions?

Our Community Forum is full of helpful information and Anvil experts.