Getting started: LiveObjects in Python
This guide shows how to integrate Ably LiveObjects into your Python application.
You will learn how to:
- Create an Ably account and get an API key for authentication.
- Install the Ably Pub/Sub SDK.
- Create a channel with LiveObjects functionality enabled.
- Use the PathObject API to access objects on a channel.
- Create, update and subscribe to changes on LiveObjects data structures: LiveMap and LiveCounter.
Authentication
An API key is required to authenticate with Ably. API keys are used either to authenticate directly with Ably using basic authentication, or to generate tokens for untrusted clients using token authentication.
Sign up for a free account and create your own API key in the dashboard or use the Control API to create an API key programmatically.
API keys and tokens have a set of capabilities assigned to them that specify which operations can be performed on which resources. The following capabilities are available for LiveObjects:
object-subscribe- grants clients read access to LiveObjects, allowing them to get the channel object and subscribe to updates.object-publish- grants clients write access to LiveObjects, allowing them to perform mutation operations on objects.
To use LiveObjects, an API key must have at least the object-subscribe capability. With only this capability, clients will have read-only access, preventing them from calling mutation methods on LiveObjects.
For the purposes of this guide, make sure your API key includes both object-subscribe and object-publish capabilities to allow full read and write access.
Install Ably Pub/Sub SDK
LiveObjects is built into the realtime client of the Ably Pub/Sub Python SDK, ably-pubsub-server, from version 4.0. No plugin is needed. The SDK requires Python 3.8 or later:
pip install ably-pubsub-serverInstantiate a client
The client is asynchronous and built on asyncio, so it must be created inside a running event loop. Instantiate an Ably Realtime client from the Pub/Sub SDK inside an async def main() coroutine, and start it with asyncio.run():
1
2
3
4
5
6
7
8
9
10
11
import asyncio
from ably.pubsub.server import ChannelMode, ChannelOptions, create_realtime_client
from ably.pubsub.server.liveobjects import LiveCounter, LiveMap
async def main():
pubsub_realtime_client = create_realtime_client(key='demokey:*****')
# Add the code from the following steps here
asyncio.run(main())Add the code from each of the following steps inside main().
Other client options can be passed as keyword arguments to create_realtime_client() to further customize the connection, however at a minimum you must set an API key. LiveObjects writes require the echo_messages client option, which is enabled by default.
Create a channel
LiveObjects is managed and persisted on channels. To use LiveObjects, you must first create a channel with the correct channel mode flags:
OBJECT_SUBSCRIBE- required to access objects on a channel.OBJECT_PUBLISH- required to create and modify objects on a channel.
1
2
3
4
channel = pubsub_realtime_client.channels.get(
'test-channel',
ChannelOptions(modes=[ChannelMode.OBJECT_PUBLISH, ChannelMode.OBJECT_SUBSCRIBE]),
)Get the channel object
The channel.object attribute gives access to the LiveObjects API for a channel.
Use channel.object.get() to obtain the channel object. The channel object is a LiveMap that always exists on a channel and acts as the top-level entry point for accessing and persisting objects. It is returned as a LiveMapPathObject, which provides a path-based API for accessing and manipulating the object hierarchy.
channel.object.get() is a coroutine that returns once the LiveObjects state is synchronized with the Ably system. Mutations are coroutines too, so you await them, while reads are synchronous and local:
1
root_object = await channel.object.get()Create and assign objects
You can create and assign objects using the LiveMap.create() and LiveCounter.create() static methods. These methods return a description of the object to create; the actual object is created when you assign it to a key with set():
1
2
3
4
5
6
7
8
9
10
11
12
# Create a LiveCounter with initial value 0 and assign it to the 'visits' key on the channel object
await root_object.set('visits', LiveCounter.create(0))
# Create a LiveMap with initial entries and assign it to the 'reactions' key on the channel object
await root_object.set('reactions', LiveMap.create({
'likes': 0,
'hearts': 0,
}))
# Infer types for the assigned objects
visits_counter = root_object.get('visits').as_live_counter()
reactions_map = root_object.get('reactions').as_live_map()root_object.get(key) returns a general PathObject that doesn't yet know the type of the value it points to. Call as_live_counter(), as_live_map() or as_primitive() to work with the value as a specific type. These views are always safe to call: they never raise an exception, even if the value at the path has a different type. Learn more about Type inference.
Subscribe to updates
Subscribe to realtime updates using the subscribe() method on a PathObject. You will be notified when the object at that path is updated by other clients or by you:
1
2
3
4
5
6
7
8
9
10
11
# Subscribe to counter updates
def on_visits_update(event):
print('Visits counter updated:', event.object.as_live_counter().value())
visits_subscription = visits_counter.subscribe(on_visits_update)
# Subscribe to map updates
def on_reactions_update(event):
print('Reactions updated:', event.object.compact_json())
reactions_subscription = reactions_map.subscribe(on_reactions_update)The listener is a regular function, called synchronously with an event that exposes:
object: APathObjectpointing to the path where the change occurredmessage: TheObjectMessagethat carried the operation that led to the change, orNone
The subscribe() method returns a Subscription object. When you no longer want to receive updates, call unsubscribe() on it:
1
2
# Later, stop receiving updates
visits_subscription.unsubscribe()Update objects
Update objects using mutation methods on PathObject. All subscribers (including you) will be notified of the changes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Update counter
await visits_counter.increment(5)
# console: Visits counter updated: 5.0
await visits_counter.decrement(2)
# console: Visits counter updated: 3.0
# Update map
await reactions_map.set('likes', 10)
# console: Reactions updated: {'likes': 10.0, 'hearts': 0.0}
await reactions_map.set('hearts', 5)
# console: Reactions updated: {'likes': 10.0, 'hearts': 5.0}
await reactions_map.remove('likes')
# console: Reactions updated: {'hearts': 5.0}Next steps
This quickstart introduced the basic concepts of LiveObjects and demonstrated how the path-based API works. The next steps are to:
- Learn about the PathObject and Instance APIs.
- Read more about LiveCounter and LiveMap.
- Learn about Objects Lifecycle Events.
- Learn about Type inference for your LiveObjects.