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.
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 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
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
SuspensionError
, it means the Javascript compiler does not support blocking code at that location.The following are considered blocking in Python:
sleep()
anvil.server.call()
anvil.users.get_user()
- Interacting with Data Tables
- Importing less common modules from the Python standard library may block
- Interacting with
anvil.js
when the Javascript returns aPromise
e.g.anvil.js.import_from(url)
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.