Skip to content

API reference

This page documents the key functions and classes available when building uproot experiments. All of these are available automatically in app modules via from uproot.smithereens import * and from uproot.fields import *.

Page types

Page

The standard page type. Displays a template and optionally collects form data.

class MyPage(Page):
    fields = dict(participant_name=StringField(label="Your name"))

    @classmethod
    def templatevars(page, player):
        return dict(greeting="Hello")

See Page methods for all available methods.

NoshowPage

A page that runs logic without displaying anything to the participant.

class Calculate(NoshowPage):
    @classmethod
    def after_always_once(page, player):
        player.payoff = player.correct * 10

GroupCreatingWait

A wait page that forms groups of participants.

class GroupPlease(GroupCreatingWait):
    group_size = 2

    @classmethod
    def after_grouping(page, group):
        for player, role in zip(group.players, ["proposer", "responder"]):
            player.role = role

SynchronizingWait

A wait page that synchronizes group or session members.

class Sync(SynchronizingWait):
    @classmethod
    def all_here(page, group):
        for player in group.players:
            set_payoff(player)

Set synchronize = "session" to synchronize across the entire session.

SmoothOperators

Random

Shuffles pages into a random order per participant.

page_order = [Random(TaskA, TaskB, TaskC)]

Bracket

Groups pages as an atomic unit within Random.

page_order = [Random(Bracket(Intro1, Task1), Bracket(Intro2, Task2))]

Rounds

Repeats pages a fixed number of times. Sets player.round (1-indexed).

page_order = [Rounds(Decision, Feedback, n=5)]

Repeat

Repeats pages indefinitely until player.add_round = False.

page_order = [Repeat(Trial, Check), Done]

Between

Randomly selects one option per participant. Records selection in player.between_showed.

page_order = [Between(Treatment, Control)]

PlayerContext

Base class for reusable computed properties. During page execution, properties on the current app’s Context class are available in Python and templates as player.context.*.

class Context(PlayerContext):
    @property
    def earnings(self):
        return self.player.payoff * C.EXCHANGE_RATE
Access How the class is selected Use it when
player.context uproot reads player.app and uses that app’s Context class A page method or template is running inside the app
Context(player) Python resolves the Context name directly in the app module Code is outside page execution, such as a callable page_order(player=) or digest(session)

player.context is None when the participant has no active app or the active app defines no Context class. See The PlayerContext class for the lifecycle details and examples.

Real-time functions

notify

Send data from one player to one or more recipients.

notify(sender, recipients, data, event="EventName", where=...)
Parameter Description
sender The sending player (determines page context)
recipients Player, StorageBunch, or list of players
data Any JSON-serializable data
event Custom event name (default: "_uproot_Received")
where Recipient page index; ... delivers regardless of page

send_to

Send data to one or more players without a sender context.

send_to(recipients, data, event="EventName", where=...)

send_to_one

Send data to a single player.

send_to_one(player, data, event="EventName", where=...)

reload

Force a player’s browser to reload the current page.

reload(player)

move_to_page

Move a player to a specific page.

move_to_page(player, TargetPage, reload_=True)

move_to_end

Move a player past all remaining pages.

move_to_end(player, reload_=True)

spawn

Run an async function as a supervised background task. The task keeps running after the calling method returns. Exceptions are logged, and all spawned tasks are cancelled at server shutdown. Tasks do not survive server restarts.

spawn(my_coroutine(player))

Because spawned tasks run outside a page method, wrap data mutations in a context manager (with player as p:). See Background tasks for examples.

Dropout functions

watch_for_dropout

Monitor a player for disconnection and call a handler when they go offline.

watch_for_dropout(player, handler, tolerance=30.0)

mark_dropout

Add a player to the manual dropout set used by the dropout watcher.

mark_dropout(player_pid)

Group functions

create_group

Create a group from a list of players.

gid = create_group(session, [player1, player2], gname="custom_name", overwrite=False)

create_groups

Create multiple groups at once.

gids = create_groups(session, [[p1, p2], [p3, p4]])

add_to_group

Add players to an existing group.

add_to_group(group, player)
add_to_group(group, [player1, player2])
add_to_group(group, player, overwrite=True)  # reassign players already in a group

The @live decorator

Makes a page method callable from JavaScript via WebSocket.

class MyPage(Page):
    @live
    async def my_method(page, player, arg: str):
        return {"result": arg.upper()}

Called from JavaScript:

uproot.invoke("my_method", "hello").then(data => console.log(data.result));

App HTTP APIs

Apps may expose HTTP endpoints by defining api(request, session) or api2(request, session).

Operator APIs

api is served at /api/{appname}/{sname}/ and requires an Authorization: Bearer ... header matching one of the server API keys in upd.API_KEYS. Use it for programmatic access that should be limited to trusted operators or scripts.

Participant APIs

api2 is served at /api2/{appname}/{sname}/. It is for participant-browser assets and callbacks, such as loading a captcha image. Direct requests remain public: anyone who knows the app name and session name can call the endpoint without participant credentials.

Trust boundary

Treat an api2 request as public unless the callback receives an authenticated player. Participant authentication identifies one participant; it does not grant operator privileges. Do not expose admin-only data or actions through api2. Use api for those.

Identifying the calling participant

This is an advanced feature. Use it when browser code must call an HTTP endpoint as the participant who is viewing the current page—for example, to upload data with fetch() while saving the result on that participant.

Define player as an optional callback argument and reject public calls explicitly:

from fastapi import HTTPException


async def api2(request, session, player=None):
    if player is None:
        raise HTTPException(status_code=403, detail="Participant required")

    data = await request.json()
    player.last_action = data["action"]
    return {"saved": True}

Call the endpoint from a participant template with uproot.api2():

<script>
async function saveAction(action) {
    const response = await uproot.api2("my_app", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({action}),
    });

    if (!response.ok) {
        throw new Error(`Request failed with status ${response.status}`);
    }

    return response.json();
}
</script>

uproot.api2(appname, options) accepts the usual fetch() options and returns its Promise<Response>. It builds the URL for the current session and adds the current participant’s name and CSRF proof as request headers. It also defaults credentials to "same-origin". Do not construct or copy these authentication headers yourself.

The server handles the three credential states differently:

Credential state Result
Both headers absent The public callback runs with request and session; the player keyword is omitted
Both headers present and valid The callback also receives the authenticated player
One header missing, either header invalid, or the CSRF proof non-ASCII The server returns 403 without running the callback

For an authenticated call, request.state.uproot_player refers to the same object as player. For a public call, it is None. The session and authenticated player remain inside their storage contexts while the callback runs, so assignments such as player.last_action = ... are saved normally.

Keep the optional default

The server omits the player keyword entirely for a public call. Write player=None if a callback may receive either kind of request. Merely adding a player parameter does not protect the endpoint—check for None and return an error as shown above.

Use only the authenticated player

Do not identify a participant from a username in the query string or request body. Those values are controlled by the caller. Read and modify only the authenticated player supplied by uproot.

Utility functions

rng

Create an independent random.Random generator with an OS-random seed. Store it on a player if you need to resume the same stream later.

player.treatment = rng().choice(["A", "B"])
player.rng = rng()
player.bomb = player.rng.randint(1, 25)

See Random numbers.

uuid

Return a UUID (uuid7 if the Python version provides it, otherwise uuid4).

player.token = str(uuid())

cu

Create a Decimal from a string. Shorthand for Decimal(value).

endowment = cu("10.00")

safe

Mark a string as HTML-safe (will not be escaped in templates).

label=StringField(label=safe("Enter a value <b>in euros</b>"))

data_uri

Convert binary data to a data URI string for embedding in HTML.

player.image_uri = data_uri(image_bytes)

The MIME type is inferred from common file signatures such as JPEG, PNG, GIF, PDF, ZIP, and MP4. Unknown data is emitted as application/octet-stream.

Identifier types

Type Description
PlayerIdentifier Uniquely identifies a player
SessionIdentifier Uniquely identifies a session
GroupIdentifier Uniquely identifies a group
ModelIdentifier Uniquely identifies a model

These are used internally and in custom data models.

StorageBunch

A collection of storage objects (players, groups) with query methods.

Method Description
len(bunch) Number of items
bunch[i] Get by index
item in bunch Membership test
bunch.filter(*.comparisons) Filter by field values using _
bunch.find_one(**kwargs) Find exactly one match
bunch.assign(key, values) Set a field on all items
bunch.each(*keys) Extract fields from all items
bunch.apply(fn) Apply a function to all items

The _ field referent

_ is a FieldReferent: a placeholder that stands for each item in the collection. It builds lazy comparison objects that are evaluated per item during filter().

# _ is auto-imported via `from uproot.smithereens import *`
cooperators = group.players.filter(_.cooperate == True)
high_earners = session.players.filter(_.payoff > 10)

# Multiple conditions (all must match)
eligible = session.players.filter(_.present == True, _.age >= 18)

# Chained attribute access
same_round = session.players.filter(_.group.round == 3)

Supported operators: ==, !=, >, >=, <, <=. Bare _.field (without an operator) tests for truthiness.

See Filtering with _ for more examples.