Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
db091b5
packaging: rename distribution to ably-pubsub-server
ttypic Sep 14, 2026
85f579d
server: build clients through factories, not constructors
ttypic Sep 15, 2026
f9417d9
pubsub: report ably-pubsub-python and ably-pubsub-server in Ably-Agent
ttypic Sep 15, 2026
1e93728
tests: improve protocol member detection and resolve client naming co…
ttypic Sep 15, 2026
1ff1592
pubsub: name the HTTP client surface after its factory
ttypic Sep 18, 2026
71fdbd0
pubsub: tie the client classes to the prototypes they implement
ttypic Sep 18, 2026
9b9ca93
pubsub: name the client classes after the prototypes they implement
ttypic Sep 18, 2026
4a869cf
test: accept ably-pubsub-python as the Ably-Agent library name
ttypic Oct 6, 2026
1059edc
feat: add batch publish and batch presence to the HTTP client
owenpearson Oct 8, 2026
b5068cc
test: run the batch specifications against the batch API
owenpearson Oct 8, 2026
0bb2550
docs: cite the batch response format issue in deviations.md
owenpearson Oct 8, 2026
d23c98c
pubsub: export the encryption helpers from ably.pubsub.server
sacOO7 Oct 16, 2026
0c75418
Merge pull request #733 from ably/feature/export-crypto-helpers
sacOO7 Oct 16, 2026
02e2edb
Merge pull request #727 from ably/feature/batch-api
owenpearson Oct 19, 2026
a920dc2
test: derive the LiveObjects unit specifications
owenpearson Oct 9, 2026
580a980
test: derive the LiveObjects integration specifications
owenpearson Oct 9, 2026
af9b3db
objects: add the LiveObjects data model and objects sync
owenpearson Oct 9, 2026
a68d184
objects: sync and publish objects over the channel
owenpearson Oct 9, 2026
e2b577a
objects: add the path-based LiveObjects API
owenpearson Oct 9, 2026
a73ab4b
objects: batch several writes into one message
owenpearson Oct 9, 2026
d2325ea
docs: record the LiveObjects derivation
owenpearson Oct 9, 2026
d5481ce
docs: add LiveObjects to the README
owenpearson Oct 9, 2026
849f65e
objects: fail sync waits on an inactive channel and skip bad messages
owenpearson Oct 9, 2026
1a6b824
objects: make compact_json the only snapshot
owenpearson Oct 14, 2026
112a4d6
liveobjects: name the package for the product
owenpearson Oct 19, 2026
98dedb3
liveobjects: export the public API from ably.pubsub.server.liveobjects
owenpearson Oct 19, 2026
4d41661
liveobjects: fail the decode of a number that is not a number
owenpearson Oct 19, 2026
469d22f
docs: count three integration tiers and clarify the RTO8a buffer tests
owenpearson Oct 19, 2026
88418b4
Merge pull request #731 from ably/feature/liveobjects
owenpearson Oct 20, 2026
6702f63
pubsub: raise 90011 when releasing a realtime channel that isn't deta…
SimonWoolf Oct 9, 2026
7032b4c
Merge pull request #729 from ably/release-throw-unless-detached
SimonWoolf Oct 20, 2026
5b5dfb6
pubsub: pass the status code and the error code the right way round
owenpearson Oct 26, 2026
4a07522
pubsub: raise AblyException for network errors and timeouts
owenpearson Oct 26, 2026
ab9c834
pubsub: keep an AblyException raised by an auth_callback as it is
owenpearson Oct 26, 2026
9a2e037
pubsub: raise a 40106 AblyException for invalid client arguments
owenpearson Oct 26, 2026
174dc84
pubsub: use token auth when a key comes with a means of getting a token
owenpearson Oct 26, 2026
165323c
pubsub: send token params to an auth_url under their wire names
owenpearson Oct 26, 2026
601eb54
pubsub: hold a realtime client's configured clientId before CONNECTED
owenpearson Oct 26, 2026
3a0cc53
pubsub: keep an explicit clientId in authorize() and copy the params
owenpearson Oct 26, 2026
1a931d5
pubsub: reject a token_details whose clientId conflicts with client_id
owenpearson Oct 26, 2026
60ddc18
pubsub: make TokenDetails.capability the server's JSON string
owenpearson Oct 26, 2026
522c31e
pubsub: stop inventing expires and issued for a bare token string
owenpearson Oct 26, 2026
70a1519
pubsub: apply default_token_params in create_token_request()
owenpearson Oct 26, 2026
ed2a857
pubsub: accept a token that names no clientId on a configured client
owenpearson Oct 26, 2026
f893550
pubsub: let a basic-auth realtime connection enter presence for others
owenpearson Oct 26, 2026
2e32cd1
pubsub: read the effective http defaults back on the client options
owenpearson Oct 26, 2026
e35b756
pubsub: remove the per-call timeout argument from requests
owenpearson Oct 26, 2026
5b8be73
pubsub: bound the write and pool phases of an http request
owenpearson Oct 26, 2026
42d83c2
pubsub: count only fallback hosts against http_max_retry_count
owenpearson Oct 26, 2026
4aca1bd
pubsub: remove the log_level client option
owenpearson Oct 26, 2026
50f7c33
pubsub: remove the deprecated and duplicate client options
owenpearson Oct 26, 2026
1a97b10
pubsub: check publishes against a configurable max_message_size
owenpearson Oct 26, 2026
d6835be
pubsub: remove the recover client option
owenpearson Oct 26, 2026
2e5c6d4
pubsub: make PresenceMessage.timestamp milliseconds since the epoch
owenpearson Oct 26, 2026
d8c4fff
pubsub: decode the TP3 ProtocolMessage tests the way the channel does
owenpearson Oct 26, 2026
14eaa71
pubsub: make Stats.interval_time a timezone-aware UTC datetime
owenpearson Oct 26, 2026
f0b1f8f
pubsub: make HttpPaginatedResponse.error_code an int
owenpearson Oct 26, 2026
ee1fc93
pubsub: make HttpPaginatedResponse.headers a read-only mapping
owenpearson Oct 26, 2026
6e344d0
pubsub: return nothing from push removals and hide the paginated resp…
owenpearson Oct 26, 2026
c381f93
pubsub: add DevicePushDetails and ignore unknown push fields
owenpearson Oct 26, 2026
0d84c6c
pubsub: make channel options a ChannelOptions that set_options replaces
owenpearson Oct 26, 2026
bd1402e
pubsub: stop creating a channel for any unknown attribute of channels
owenpearson Oct 26, 2026
4a761b6
pubsub: encode a space in a channel name as %20 in HTTP paths
owenpearson Oct 26, 2026
eb50756
pubsub: raise from attach() when the channel does not attach
owenpearson Oct 26, 2026
7ff41eb
pubsub: detach at once when the connection is not connected
owenpearson Oct 26, 2026
b3271ef
pubsub: defer a ping from DISCONNECTED and time it from the heartbeat
owenpearson Oct 26, 2026
72d00f8
pubsub: return from set_options once an attached channel reattaches
owenpearson Oct 26, 2026
9ec2366
pubsub: take the millisecond timing options in seconds, as *_seconds
owenpearson Oct 28, 2026
2c03eea
pubsub: name the HTTP timing options for their unit, as *_seconds
owenpearson Oct 28, 2026
f8bfced
pubsub: name the replacement when a renamed timing option is passed
owenpearson Oct 28, 2026
ba567a0
pubsub: validate the timing options when the client is created
owenpearson Oct 28, 2026
585d28e
pubsub: send a timedelta ttl to the millisecond
owenpearson Oct 28, 2026
36020ca
pubsub: make PresenceAction an IntEnum
owenpearson Oct 28, 2026
c68eb85
pubsub: name the four remaining sync handle classes with a Sync suffix
owenpearson Oct 28, 2026
e45fd32
pubsub: export the public types from ably.pubsub.server and its sync …
owenpearson Oct 28, 2026
32af1b9
Merge pull request #737 from ably/v4-exceptions
owenpearson Oct 29, 2026
588afdf
Merge pull request #738 from ably/v4-auth-tokens
owenpearson Oct 29, 2026
956f62d
Merge pull request #739 from ably/v4-http-client-options
owenpearson Oct 29, 2026
af940e6
Merge pull request #740 from ably/v4-values-types
owenpearson Oct 29, 2026
f942a05
Merge pull request #741 from ably/v4-channels
owenpearson Oct 29, 2026
9b06676
Merge pull request #742 from ably/v4-durations-seconds
owenpearson Oct 29, 2026
be0e534
Merge pull request #743 from ably/v4-exports
owenpearson Oct 30, 2026
3088bb1
Merge remote-tracking branch 'origin/main' into integration/v4
ttypic Nov 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Then perform these steps in order:
1. Run `git checkout -b release/NEW_VERSION`
2. Replace `OLD_VERSION` with `NEW_VERSION` in all of the following files:
- `pyproject.toml` — the `version` property
- `ably/__init__.py` — lib_version value
- `ably/pubsub/version.py` — lib_version value
3.Run `uv sync` to update `uv.lock` file
4. Commit all files together with message: `chore: bump version to NEW_VERSION`
3. Fetch merged PRs since the last release tag using:
Expand Down
164 changes: 134 additions & 30 deletions .claude/skills/uts-to-python/SKILL.md

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,27 @@ jobs:
with:
name: python-package-distributions
path: dist/
- name: Check that wheel and tarball contains ably/sync/
- name: Check that wheel and tarball contains ably/pubsub/sync/
Comment thread
ttypic marked this conversation as resolved.
Resolved
run: |
# Check wheel
WHEEL=$(ls dist/*.whl | head -n 1)
echo "Checking wheel: $WHEEL"
if unzip -l "$WHEEL" | grep -q "ably/sync/"; then
echo "✅ Found ably/sync/ in wheel"
if unzip -l "$WHEEL" | grep -q "ably/pubsub/sync/"; then
echo "✅ Found ably/pubsub/sync/ in wheel"
else
unzip -l "$WHEEL"
echo "❌ ably/sync/ not found in wheel"
echo "❌ ably/pubsub/sync/ not found in wheel"
exit 1
fi

# Check tarball
TARBALL=$(ls dist/*.tar.gz | head -n 1)
echo "Checking tarball: $TARBALL"
if tar -tzf "$TARBALL" | grep -q "ably/sync/"; then
echo "✅ Found ably/sync/ in tarball"
if tar -tzf "$TARBALL" | grep -q "ably/pubsub/sync/"; then
echo "✅ Found ably/pubsub/sync/ in tarball"
else
tar -tzf "$TARBALL"
echo "❌ ably/sync/ not found in tarball"
echo "❌ ably/pubsub/sync/ not found in tarball"
exit 1
fi

Expand All @@ -74,7 +74,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/ably
url: https://pypi.org/p/ably-pubsub-server
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing

Expand All @@ -94,7 +94,7 @@ jobs:
- name: Read VERSION_NAME from dist/
id: version
run: |
VERSION_NAME=$(basename dist/ably-*.tar.gz | sed -E 's/^ably-([^-]+)\.tar\.gz$/\1/')
VERSION_NAME=$(basename dist/ably_pubsub_server-*.tar.gz | sed -E 's/^ably_pubsub_server-([^-]+)\.tar\.gz$/\1/')
echo "version=$VERSION_NAME" >> $GITHUB_OUTPUT

- name: Compare version with tag
Expand All @@ -118,7 +118,7 @@ jobs:

environment:
name: testpypi
url: https://test.pypi.org/p/ably
url: https://test.pypi.org/p/ably-pubsub-server

permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ ably/types/options.py.orig
test/ably/restsetup.py.orig

.idea/**/*
ably/sync/**
ably/pubsub/sync/**
test/ably/sync/**
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ arithmetic on the unix epoch, are not clock readings and stay as they are.

`asyncio.wait_for` in `ConnectionManager.ping` is the one delay that runs on the event
loop's clock rather than this one; a test which waits it out sets a short real
`realtime_request_timeout`.
`realtime_request_timeout_seconds`.

The timing client options are seconds, named `*_seconds`, where the clock counts
milliseconds. `Options` converts them in one place, `seconds_to_ms`, behind a private
`_<option>_ms` property for each, and code scheduling on the clock reads those.

`Auth._timestamp` signs a token request the server validates, so a client which reads a
fake clock talks to a mock rather than to the sandbox.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The release process must include the following steps:

1. Ensure that all work intended for this release has landed to `main`
2. Create a release branch named like `release/2.0.1`
3. Add a commit to bump the version number, updating [`pyproject.toml`](./pyproject.toml) and [`ably/__init__.py`](./ably/__init__.py)
3. Add a commit to bump the version number, updating [`pyproject.toml`](./pyproject.toml) and [`ably/pubsub/version.py`](./ably/pubsub/version.py)
4. Run [`github_changelog_generator`](https://github.com/github-changelog-generator/github-changelog-generator) to automate the update of the [CHANGELOG](./CHANGELOG.md). This may require some manual intervention, both in terms of how the command is run and how the change log file is modified. Your mileage may vary:
- The command you will need to run will look something like this: `github_changelog_generator -u ably -p ably-pubsub-python --since-tag v2.0.0 --output delta.md --token $GITHUB_TOKEN_WITH_REPO_ACCESS`. Generate token [here](https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token).
- Using the command above, `--output delta.md` writes changes made after `--since-tag` to a new file
Expand Down
15 changes: 11 additions & 4 deletions LONG_DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
Official Ably Bindings for Python
==================================
Official Ably Pub/Sub Bindings for Python Servers
=================================================

A Python client library for Ably Realtime messaging.
A Python client library for Ably Pub/Sub realtime messaging, for use from
servers.


Setup
-----

You can install this package by using the pip tool and installing:

pip install ably
pip install ably-pubsub-server

The package installs into ``ably.pubsub``. Both ``ably`` and ``ably.pubsub``
are namespace packages shared with the other ``ably-*`` distributions, so the
whole public API is reached through ``ably.pubsub.server``::

from ably.pubsub.server import create_http_client, create_realtime_client


Using Ably for Python
Expand Down
77 changes: 74 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
![Ably Pub/Sub Python Header](images/pythonSDK-github.png)
[![PyPI version](https://badge.fury.io/py/ably.svg)](https://pypi.org/project/ably/)
[![PyPI version](https://badge.fury.io/py/ably-pubsub-server.svg)](https://pypi.org/project/ably-pubsub-server/)
[![License](https://img.shields.io/github/license/ably/ably-pubsub-python)](https://github.com/ably/ably-pubsub-python/blob/main/LICENSE)


Expand Down Expand Up @@ -48,9 +48,31 @@ The following platforms are supported:
To get started with your project, install the package:

```sh
pip install ably
pip install ably-pubsub-server
```

The package installs into `ably.pubsub`, and the whole public API is reached
through `ably.pubsub.server`:

```python
from ably.pubsub.server import create_http_client, create_realtime_client
```

Clients are built by these factories rather than by constructing a class, so
that the package a client comes from names the side your application runs on.
Annotate against the prototypes they return, `PubSubHttpClient` and
`PubSubRealtimeClient`.

The synchronous, HTTP-only flavour lives alongside it:

```python
from ably.pubsub.server.sync import create_http_client
```

Both `ably` and `ably.pubsub` are [namespace packages](https://peps.python.org/pep-0420/)
shared with the other `ably-*` distributions, so neither exports anything
itself — always import from `ably.pubsub.server`.

> [!NOTE]
Install [Python](https://www.python.org/downloads/) version 3.8 or greater.

Expand All @@ -59,8 +81,10 @@ Install [Python](https://www.python.org/downloads/) version 3.8 or greater.
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.

```python
from ably.pubsub.server import create_realtime_client

# Initialize Ably Realtime client
async with AblyRealtime('your-ably-api-key', client_id='me') as realtime_client:
async with create_realtime_client(key='your-ably-api-key', client_id='me') as realtime_client:
# Wait for connection to be established
await realtime_client.connection.once_async('connected')
print('Connected to Ably')
Expand All @@ -78,6 +102,53 @@ async with AblyRealtime('your-ably-api-key', client_id='me') as realtime_client:
await channel.publish('test-event', 'hello world')
```

### LiveObjects

LiveObjects keeps shared, mutable state on a channel: maps and counters that every client
attached to it reads, updates and subscribes to. The channel needs the object modes, and
`channel.object.get()` attaches it, waits for the objects to sync, and returns the root map.
Values are read through a typed view of their path — `as_live_map()`, `as_live_counter()` or
`as_primitive()` — and reads are synchronous; writes are awaited.

```python
from ably.pubsub.server import ChannelMode, ChannelOptions, create_realtime_client
from ably.pubsub.server.liveobjects import LiveCounter, LiveMap

async with create_realtime_client(key='your-ably-api-key') as realtime_client:
channel = realtime_client.channels.get(
'my-objects',
ChannelOptions(modes=[ChannelMode.OBJECT_SUBSCRIBE, ChannelMode.OBJECT_PUBLISH]),
)

# Attach, wait for the objects to sync, and get the root map
root = await channel.object.get()

# Create objects by setting them on the root
await root.set('visits', LiveCounter.create(0))
await root.set('profile', LiveMap.create({'name': 'Alice', 'theme': 'dark'}))

# Read through a typed view of the path
visits = root.get('visits').as_live_counter()
print(visits.value()) # 0.0
print(root.at('profile.name').as_primitive().value(str)) # Alice

# Subscribe to changes at a path
def on_change(event):
print(f'{event.object.path()} changed')

subscription = root.get('visits').subscribe(on_change)

# Mutate
await visits.increment(5)

# Batch several writes into a single message
async with root.get('profile').as_live_map().batch() as profile:
profile.set('name', 'Bob')
profile.remove('theme')

subscription.unsubscribe()
```

## Releases

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).
Expand Down
Loading
Loading