> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useterse.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> Define actors, generate typed clients, and stream output.

## Install

Requires Python 3.11 or later. The Terse CLI also requires Node.js.

```sh theme={null}
uv add durable-actors
uv add --dev 'durable-actors[codegen]'
```

Define actors in `src/actor.py`:

```python theme={null}
from durable_actors import Actor, ActorSocket, emitted
from pydantic import BaseModel


class Member(BaseModel):
    user_id: str


class Change(BaseModel):
    by: int


class Update(BaseModel):
    count: int


class Counter(Actor[Member, Change, Update]):
    count: int = emitted(0)

    def increment(self, by: int) -> int:
        self.count += by
        return self.count

    def on_message(self, socket: ActorSocket[Member, Update], message: Change) -> None:
        self.increment(message.by)
        self.broadcast(Update(count=self.count))
```

## Actor definitions

Extend `Actor` directly. Use typed synchronous `def` methods. Use defaults or factories for fields. Prefix helper methods with `_`.

| API | Purpose |
| - | - |
| `Actor[Metadata, Incoming, Outgoing, Tag]` | Type connection metadata, messages, and optional tags. |
| Annotated field with a default | Persist state after successful calls. |
| `emitted(default)` | Persist a field and emit saved state. |
| `emitted(default_factory=list)` | Create a separate mutable default per actor. |
| `ephemeral(default)` | Keep a field in memory only. |
| `ephemeral(default_factory=...)` | Create a temporary client, lock, or cache. |
| `@reentrant` | Allow overlapping calls on worker threads. |
| `self.id` | Read the bound actor ID during a call. |

Use concrete public types. JSON primitives, typed collections, unions, and Pydantic models work. `Any` and untyped collections fail contract generation.

## Backend calls

Run from a Python application with `pyproject.toml`:

```sh theme={null}
terse actor generate --language python
```

```python theme={null}
from generated import actors

counter = actors.Counter.get("visits")
count = counter.increment(1)
```

Generated calls are synchronous. Use a worker thread from an async web framework.

The namespace exposes `actors.Counter.Stub`, `actors.Counter.State`, message models, and method argument and result types.

Set `TERSE_ACTOR_URL` and `TERSE_API_KEY` on the calling backend for hosted actors. Load `.env` explicitly; `uv run --env-file .env` can do that.

## Browser access

Authenticate the user. Check access to the actor ID. Derive metadata on your backend. Issue a grant:

```python theme={null}
from generated import ActorProxy, actors

grant = ActorProxy.handle(actors.Counter.Authorization(
    actor_id="visits",
    metadata=actors.Counter.Metadata(user_id="authorized-user"),
))
print(grant.websocket_url)
```

Return the URL with `Cache-Control: no-store`. The browser opens a WebSocket. It sends JSON messages such as `{"by": 1}`.

## Socket handlers

| API | Purpose |
| - | - |
| `on_connect(socket)` | Accept a connection or reject it. |
| `on_message(socket, message)` | Handle a validated application message. |
| `on_disconnect(socket, code, reason, was_clean)` | Handle disconnection. A process failure can skip this hook. |
| `socket.metadata` | Read backend-supplied metadata. |
| `socket.send(message)` | Send to one connection. |
| `socket.reject(4003, "Forbidden")` | Reject during `on_connect`. |
| `socket.close(1000, "Done")` | Close the connection. |
| `socket.set_tags(*tags)` | Set connection tags. |
| `self.get_connections()` | List connections during a call. |
| `self.broadcast(message)` | Send to all connections, including the sender. |

Broadcast filters accept `except_ids`, `tags`, and `tag_match="all"` or `"any"`.

## Streaming and subscriptions

Use the provider's synchronous stream inside the handler. Broadcast each output update. Save the completed result before returning.

Browser clients receive `state` snapshots and `state_update` messages for emitted fields. Replace snapshots. Merge updates. Remove fields listed in `removed`.

Python clients can subscribe to complete typed state:

```python theme={null}
with counter.subscribe(lambda state: print(state.count)):
    counter.increment(1)
    input("Press Enter to close.")
```

Subscription callbacks run on a background thread. Errors stop the subscription and reach `on_error`.

For application messages, use `counter.connect(actors.Counter.Metadata(user_id="authorized-user"))` as a context manager. Call `connection.send(...)`. Iterate over the connection. Handle `StateSnapshot` and `StateUpdate` alongside application messages.

Broadcasts have no replay. Reconnect and restore saved state. See the [quickstart](/quickstart).

## Failures and concurrency

Ordinary calls serialize. Failed calls roll back persisted fields. External writes and broadcasts can already have happened.

`ActorInvocationError` with code `outcome_unknown` means the call may have executed. Reconcile before retrying.

`@reentrant` allows concurrent worker threads. It disables failure rollback across the actor class. Protect shared mutations with an ephemeral lock. See [Concepts](/faqs#why-are-durable-actors-sequential-by-default-how-do-i-disable-that).
