Skip to content

Commit 60c46e4

Browse files
authored
Merge pull request #745 from ably/docs/readme-4.0
Update README for 4.0
2 parents c000585 + d6599c7 commit 60c46e4

1 file changed

Lines changed: 198 additions & 107 deletions

File tree

‎README.md‎

Lines changed: 198 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,262 @@
1-
![Ably Pub/Sub Python Header](images/pythonSDK-github.png)
2-
[![PyPI version](https://badge.fury.io/py/ably-pubsub-server.svg)](https://pypi.org/project/ably-pubsub-server/)
3-
[![License](https://img.shields.io/github/license/ably/ably-pubsub-python)](https://github.com/ably/ably-pubsub-python/blob/main/LICENSE)
1+
![Ably Pub/Sub Python Header](https://raw.githubusercontent.com/ably/ably-pubsub-python/main/images/pythonSDK-github.png)
42

3+
[![PyPI: ably-pubsub-server](https://img.shields.io/pypi/v/ably-pubsub-server?label=ably-pubsub-server)](https://pypi.org/project/ably-pubsub-server/)
4+
[![License](https://badgen.net/github/license/ably/ably-pubsub-python)](https://github.com/ably/ably-pubsub-python/blob/main/LICENSE)
55

66
# Ably Pub/Sub Python SDK
77

8-
Build any realtime experience using Ably’s Pub/Sub Python SDK.
8+
The Python SDK for [Ably Pub/Sub](https://ably.com/docs/basics): publish and subscribe to channels,
9+
and use message history, presence and LiveObjects, from servers.
910

10-
Ably Pub/Sub provides flexible APIs that deliver features such as pub-sub messaging, message history, presence, and push notifications. Utilizing Ably’s realtime messaging platform, applications benefit from its highly performant, reliable, and scalable infrastructure.
11+
## Overview
1112

12-
Find out more:
13+
This SDK provides the server package only, for trusted backends authenticating with an API key.
14+
There is no device package; for end-user devices, use a device SDK such as
15+
[JavaScript](https://github.com/ably/ably-pubsub-js), [Swift/Objective-C](https://github.com/ably/ably-pubsub-cocoa),
16+
[Java/Android](https://github.com/ably/ably-pubsub-java), [.NET](https://github.com/ably/ably-pubsub-dotnet)
17+
or [Flutter](https://github.com/ably/ably-pubsub-flutter).
1318

14-
* [Ably Pub/Sub docs.](https://ably.com/docs/basics)
15-
* [Ably Pub/Sub examples.](https://ably.com/examples?product=pubsub)
19+
| Package | Use it for | Entry point |
20+
|---------|-----------|-------------|
21+
| `ably-pubsub-server` | Servers and other trusted backend environments | `create_realtime_client`, `create_http_client` |
1622

17-
---
23+
## Requirements
1824

19-
## Getting started
25+
Python 3.8 or later (Linux, macOS and Windows).
2026

21-
Everything you need to get started with Ably:
27+
## Installation
2228

23-
* [Getting started with Pub/Sub using Python.](https://ably.com/docs/getting-started/python)
24-
* [SDK Setup for Python.](https://ably.com/docs/getting-started/setup?lang=python)
29+
```bash
30+
pip install ably-pubsub-server
31+
```
2532

26-
---
33+
For channel encryption or delta compression, install the `crypto` or `vcdiff` extra, for example
34+
`pip install "ably-pubsub-server[crypto]"`.
2735

28-
## Supported platforms
36+
## Quick start
2937

30-
Ably aims to support a wide range of platforms. If you experience any compatibility issues, open an issue in the repository or contact [Ably support](https://ably.com/support).
38+
Use an API key on a server. The samples read it from the `ABLY_API_KEY` environment variable; set it
39+
to an API key from your [Ably dashboard](https://ably.com/accounts/any/apps/any/app_keys). The
40+
following code connects, subscribes to a channel, publishes a message to it and waits for the
41+
message to arrive.
3142

32-
The following platforms are supported:
43+
```python
44+
import asyncio
45+
import os
3346

34-
| Platform | Support |
35-
|----------|--------------------------|
36-
| Python | Python 3.7+ through 3.14 |
47+
from ably.pubsub.server import create_realtime_client
3748

38-
> [!NOTE]
39-
> This SDK works across all major operating platforms (Linux, macOS, Windows) as long as Python 3.7+ is available.
4049

41-
> [!IMPORTANT]
42-
> SDK versions < 2.0.0 are [deprecated](https://ably.com/docs/platform/deprecate/protocol-v1).
50+
async def main():
51+
ably_api_key = os.environ['ABLY_API_KEY']
52+
async with create_realtime_client(key=ably_api_key) as realtime:
53+
channel = realtime.channels.get('test-channel')
54+
received = asyncio.get_running_loop().create_future()
4355

44-
---
56+
def on_message(message):
57+
if not received.done():
58+
received.set_result(message)
4559

46-
## Installation
60+
# subscribe() attaches the channel and returns once it is attached.
61+
await channel.subscribe(on_message)
62+
await channel.publish('test-event', 'hello world')
4763

48-
To get started with your project, install the package:
64+
message = await received
65+
print(f'Received: {message.data}')
4966

50-
```sh
51-
pip install ably-pubsub-server
67+
68+
asyncio.run(main())
5269
```
5370

54-
The package installs into `ably.pubsub`, and the whole public API is reached
55-
through `ably.pubsub.server`:
71+
Create the realtime client inside a running event loop. Close every client with `async with` or
72+
`await client.close()`.
73+
74+
To publish over HTTP without a realtime connection, create an HTTP client instead:
5675

5776
```python
58-
from ably.pubsub.server import create_http_client, create_realtime_client
77+
import asyncio
78+
import os
79+
80+
from ably.pubsub.server import create_http_client
81+
82+
83+
async def main():
84+
ably_api_key = os.environ['ABLY_API_KEY']
85+
async with create_http_client(key=ably_api_key) as client:
86+
await client.channels.get('test-channel').publish('test-event', 'hello world')
87+
88+
89+
asyncio.run(main())
5990
```
6091

61-
Clients are built by these factories rather than by constructing a class, so
62-
that the package a client comes from names the side your application runs on.
63-
Annotate against the prototypes they return, `PubSubHttpClient` and
64-
`PubSubRealtimeClient`.
92+
For code without an event loop, `ably.pubsub.server.sync.create_http_client` creates a synchronous
93+
HTTP client. There is no synchronous realtime client.
6594

66-
The synchronous, HTTP-only flavour lives alongside it:
95+
> [!IMPORTANT]
96+
> Server clients must authenticate with an API key. To use a token instead, see [Server clients and token authentication](https://ably.com/docs/auth/token#server-token-auth).
97+
98+
The following endpoint issues token requests. Devices using a device SDK set `authUrl` to its URL,
99+
for example `https://your-server.example.com/ably-auth`:
67100

68101
```python
69-
from ably.pubsub.server.sync import create_http_client
102+
import os
103+
from contextlib import asynccontextmanager
104+
105+
from ably.pubsub.server import create_http_client
106+
from fastapi import FastAPI
107+
108+
ably_api_key = os.environ['ABLY_API_KEY']
109+
client = create_http_client(key=ably_api_key)
110+
111+
112+
@asynccontextmanager
113+
async def lifespan(app):
114+
async with client: # closes the client when the app shuts down
115+
yield
116+
117+
118+
app = FastAPI(lifespan=lifespan)
119+
120+
121+
@app.get('/ably-auth')
122+
async def ably_auth():
123+
# Authenticate the caller first. Grant only the operations this user needs.
124+
user_id = 'me' # the signed-in user's ID, from your session
125+
token_request = await client.auth.create_token_request({
126+
'client_id': user_id,
127+
'capability': {'test-channel': ['publish', 'subscribe', 'history']},
128+
})
129+
return token_request.to_dict()
70130
```
71131

72-
Both `ably` and `ably.pubsub` are [namespace packages](https://peps.python.org/pep-0420/)
73-
shared with the other `ably-*` distributions, so neither exports anything
74-
itself — always import from `ably.pubsub.server`.
132+
See [token authentication](https://ably.com/docs/auth/token) for how devices use it.
133+
134+
## Migrating from 3.x
135+
136+
Version 4.0 renames the package and creates clients with factory functions.
137+
138+
| | 3.x | 4.0 |
139+
|---|---|---|
140+
| Package | `ably` | `ably-pubsub-server` |
141+
| Import | `from ably import AblyRealtime, AblyRest` | `from ably.pubsub.server import create_realtime_client, create_http_client` |
142+
| Realtime client | `AblyRealtime('key')` | `create_realtime_client(key='key')` |
143+
| HTTP client | `AblyRest('key')` | `create_http_client(key='key')` |
144+
145+
See [UPDATING.md](https://github.com/ably/ably-pubsub-python/blob/main/UPDATING.md#version-3x-to-400) for all other changes.
146+
147+
## Common configuration
75148

76-
> [!NOTE]
77-
Install [Python](https://www.python.org/downloads/) version 3.8 or greater.
149+
Pass options to both factories as keyword arguments.
78150

79-
## Usage
151+
### Endpoint
80152

81-
The following code connects to Ably's realtime messaging service, subscribes to a channel to receive messages, and publishes a test message to that same channel.
153+
`endpoint` selects the Ably host for HTTP requests and the realtime connection. It replaces the
154+
3.x `environment`, `rest_host` and `realtime_host` options.
82155

83156
```python
84-
from ably.pubsub.server import create_realtime_client
157+
ably_api_key = os.environ['ABLY_API_KEY']
158+
realtime = create_realtime_client(key=ably_api_key, endpoint='nonprod:sandbox')
159+
```
160+
161+
- Never put a port in `endpoint`: `localhost:8080` is not a hostname and does not resolve.
162+
- Set the port with `tls_port`, or with `port` when TLS is off.
163+
- With TLS off (`tls=False`), use token authentication.
164+
165+
See [UPDATING.md](https://github.com/ably/ably-pubsub-python/blob/main/UPDATING.md#deprecated-and-duplicate-options-are-removed) for how the old
166+
options map to `endpoint`.
167+
168+
### Logging
169+
170+
The SDK logs through Python's standard [`logging`](https://docs.python.org/3/library/logging.html)
171+
module, to the `ably.pubsub` logger. Set the level and add a handler on that logger:
85172

86-
# Initialize Ably Realtime client
87-
async with create_realtime_client(key='your-ably-api-key', client_id='me') as realtime_client:
88-
# Wait for connection to be established
89-
await realtime_client.connection.once_async('connected')
90-
print('Connected to Ably')
91-
92-
# Get a reference to the 'test-channel' channel
93-
channel = realtime_client.channels.get('test-channel')
94-
95-
# Subscribe to all messages published to this channel
96-
def on_message(message):
97-
print(f'Received message: {message.data}')
98-
99-
await channel.subscribe(on_message)
100-
101-
# Publish a test message to the channel
102-
await channel.publish('test-event', 'hello world')
173+
```python
174+
import logging
175+
176+
logger = logging.getLogger('ably.pubsub')
177+
logger.setLevel(logging.DEBUG)
178+
logger.addHandler(logging.StreamHandler())
103179
```
104180

105-
### LiveObjects
181+
The 3.x `log_level` option is [removed](https://github.com/ably/ably-pubsub-python/blob/main/UPDATING.md#the-log_level-option-is-removed).
182+
183+
### Delta compression
106184

107-
LiveObjects keeps shared, mutable state on a channel: maps and counters that every client
108-
attached to it reads, updates and subscribes to. The channel needs the object modes, and
109-
`channel.object.get()` attaches it, waits for the objects to sync, and returns the root map.
110-
Values are read through a typed view of their path — `as_live_map()`, `as_live_counter()` or
111-
`as_primitive()` — and reads are synchronous; writes are awaited.
185+
To receive [delta-compressed](https://ably.com/docs/channels/options/deltas) messages:
186+
187+
- Install the `vcdiff` extra.
188+
- Pass an `AblyVCDiffDecoder` to the realtime client as `vcdiff_decoder`.
189+
- Set the `delta` channel parameter to `vcdiff`.
112190

113191
```python
114-
from ably.pubsub.server import ChannelMode, ChannelOptions, create_realtime_client
115-
from ably.pubsub.server.liveobjects import LiveCounter, LiveMap
192+
import os
116193

117-
async with create_realtime_client(key='your-ably-api-key') as realtime_client:
118-
channel = realtime_client.channels.get(
119-
'my-objects',
120-
ChannelOptions(modes=[ChannelMode.OBJECT_SUBSCRIBE, ChannelMode.OBJECT_PUBLISH]),
121-
)
194+
from ably.pubsub.server import AblyVCDiffDecoder, ChannelOptions, create_realtime_client
122195

123-
# Attach, wait for the objects to sync, and get the root map
124-
root = await channel.object.get()
196+
ably_api_key = os.environ['ABLY_API_KEY']
197+
realtime = create_realtime_client(key=ably_api_key, vcdiff_decoder=AblyVCDiffDecoder())
198+
channel = realtime.channels.get('test-channel', ChannelOptions(params={'delta': 'vcdiff'}))
199+
```
125200

126-
# Create objects by setting them on the root
127-
await root.set('visits', LiveCounter.create(0))
128-
await root.set('profile', LiveMap.create({'name': 'Alice', 'theme': 'dark'}))
201+
## Live Objects
129202

130-
# Read through a typed view of the path
131-
visits = root.get('visits').as_live_counter()
132-
print(visits.value()) # 0.0
133-
print(root.at('profile.name').as_primitive().value(str)) # Alice
203+
Use [LiveObjects](https://ably.com/docs/liveobjects) through `channel.object`. Get the channel with
204+
the `OBJECT_SUBSCRIBE` and `OBJECT_PUBLISH` modes. `channel.object.get()` waits for the objects to
205+
sync and returns the root map.
134206

135-
# Subscribe to changes at a path
136-
def on_change(event):
137-
print(f'{event.object.path()} changed')
207+
```python
208+
import asyncio
209+
import os
138210

139-
subscription = root.get('visits').subscribe(on_change)
211+
from ably.pubsub.server import ChannelMode, ChannelOptions, create_realtime_client
212+
from ably.pubsub.server.liveobjects import LiveCounter
140213

141-
# Mutate
142-
await visits.increment(5)
143214

144-
# Batch several writes into a single message
145-
async with root.get('profile').as_live_map().batch() as profile:
146-
profile.set('name', 'Bob')
147-
profile.remove('theme')
215+
async def main():
216+
ably_api_key = os.environ['ABLY_API_KEY']
217+
async with create_realtime_client(key=ably_api_key) as realtime:
218+
channel = realtime.channels.get(
219+
'test-channel',
220+
ChannelOptions(modes=[ChannelMode.OBJECT_SUBSCRIBE, ChannelMode.OBJECT_PUBLISH]),
221+
)
222+
root = await channel.object.get()
148223

149-
subscription.unsubscribe()
150-
```
224+
await root.set('visits', LiveCounter.create(0))
225+
visits = root.get('visits').as_live_counter()
226+
await visits.increment(5)
227+
print(f'Visits: {visits.value()}')
151228

152-
## Releases
153229

154-
The [CHANGELOG.md](https://github.com/ably/ably-pubsub-python/blob/main/CHANGELOG.md) contains details of the latest releases for this SDK. You can also view all Ably releases on [changelog.ably.com](https://changelog.ably.com).
230+
asyncio.run(main())
231+
```
232+
233+
## Known limitations
155234

156-
---
235+
- Connection recovery (the 3.x `recover` option) is not supported.
236+
- Subscription filters and derived channels are not supported.
237+
- Realtime presence has no `history()`. Use `channel.presence.history()` on an HTTP client.
238+
- [Token revocation](https://ably.com/docs/auth/revocation) is not supported.
239+
- A Python client cannot be a [push notification](https://ably.com/docs/push) target. The push
240+
admin API is supported.
157241

158-
## Contribute
242+
## Further reading
159243

160-
Read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines to contribute to Ably.
244+
- [Getting started with Pub/Sub using Python](https://ably.com/docs/getting-started/python)
245+
- [Ably Pub/Sub documentation](https://ably.com/docs/basics) and [examples](https://ably.com/examples?product=pubsub)
246+
- [Token authentication](https://ably.com/docs/auth/token)
247+
- [UPDATING.md](https://github.com/ably/ably-pubsub-python/blob/main/UPDATING.md): migrating from 3.x
248+
- [CHANGELOG.md](https://github.com/ably/ably-pubsub-python/blob/main/CHANGELOG.md): release notes; all Ably releases are listed on [changelog.ably.com](https://changelog.ably.com)
161249

162-
---
250+
## Contributing
163251

164-
## Support, feedback, and troubleshooting
252+
Read [CONTRIBUTING.md](https://github.com/ably/ably-pubsub-python/blob/main/CONTRIBUTING.md) before contributing; it also describes the release
253+
process.
165254

166-
For help or technical support, visit Ably's [support page](https://ably.com/support) or [GitHub Issues](https://github.com/ably/ably-pubsub-python/issues) for community-reported bugs and discussions.
255+
## Support, feedback and troubleshooting
167256

168-
### Full Realtime support unavailable
257+
For help, visit Ably's [support page](https://ably.com/support). Report bugs and compatibility
258+
problems on [GitHub Issues](https://github.com/ably/ably-pubsub-python/issues).
169259

170-
This SDK currently supports only [Ably REST](https://ably.com/docs/rest) and basic realtime message subscriptions. To access full [Ably Realtime](https://ably.com/docs/realtime) features in Python, consider using the [MQTT adapter](https://ably.com/docs/mqtt).
260+
## License
171261

262+
Licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/ably/ably-pubsub-python/blob/main/LICENSE).

0 commit comments

Comments
 (0)