Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
# Change Log

## [4.0.0](https://github.com/ably/ably-python/tree/v4.0.0) (unreleased)

[Full Changelog](https://github.com/ably/ably-python/compare/v3.1.2...v4.0.0)

> This entry describes work in progress on the `integration/v4` branch. Nothing
> is published under these names yet; the heading loses "(unreleased)" when
> 4.0.0 ships.

### Breaking change

Version 4.0.0 applies [PDR-091b](https://ably.atlassian.net/wiki/spaces/product/pages/5362810886)
and splits the SDK into new distributions. The `ably` package is superseded: it
receives security and critical-bug fixes only for one year from the 4.0.0
release date, and is then end-of-life.

- **New distributions.** `ably-pubsub-server` is the public package for servers
and other trusted environments. It is built on `ably-pubsub-core`, an internal
package that must never be depended on directly; the two are versioned and
released in lockstep, with the server pinning the core to the exact same
version.
- **New import namespace.** `ably` becomes `ably_pubsub.server` (and
`ably.sync` becomes `ably_pubsub.server.sync`). Both installs can coexist in
one environment while migrating, since the import packages differ.
- **Factory doors replace the constructors.** `AblyRest(...)` becomes
`ably_pubsub.server.create_http_client(...)`, `AblyRealtime(...)` becomes
`ably_pubsub.server.create_realtime_client(...)`, and `AblyRestSync(...)`
becomes `ably_pubsub.server.sync.create_http_client(...)`. Each takes exactly
the arguments the constructor it replaces took.
- **Agent identifier.** Clients now report
`ably-pubsub-python/4.0.0 python/<version> ably-pubsub-server`. The
`ably-pubsub-server` flag is what declares the server side, which is how the
platform exempts these connections from monthly-active-user counting. A new
`agents` client option lets a layered SDK add its own entries.
- **Python floor raised.** `requires-python` is now `>=3.8`; supported versions
are 3.8 through 3.14.
- **`LONG_DESCRIPTION.rst` removed.** Each distribution's PyPI page now renders
its own `README.md`.

For detailed migration instructions, please refer to the [Upgrading Guide](UPDATING.md).

## [3.1.2](https://github.com/ably/ably-python/tree/v3.1.2)

[Full Changelog](https://github.com/ably/ably-python/compare/v3.1.1...v3.1.2)
Expand Down
123 changes: 102 additions & 21 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-python)](https://github.com/ably/ably-python/blob/main/LICENSE)


Expand All @@ -25,6 +25,28 @@ Everything you need to get started with Ably:

---

## Packages

Python is a server-side language, so this repository publishes one package for
applications to install, plus the shared implementation it is built on:

| Distribution | For | Import |
| --- | --- | --- |
| `ably-pubsub-server` | Servers, backends, jobs — any trusted environment holding an API key | `ably_pubsub.server` |
| `ably-pubsub-core` | **Internal.** Ably's own packages only — never depend on it directly | — |

Installing the package that names the side your code runs on is more than a
naming convention: clients created by `ably-pubsub-server` declare themselves as
server-side on the wire, and server connections are exempt from monthly-active-user
counting.

Nothing under `ably_pubsub.core` is public API — its module layout and the names
within it may change in any release. Everything supported is re-exported from
`ably_pubsub.server`. The two distributions are versioned and released in
lockstep: `ably-pubsub-server` pins `ably-pubsub-core` to the exact same version.

---

## Supported platforms

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).
Expand All @@ -33,13 +55,13 @@ The following platforms are supported:

| Platform | Support |
|----------|--------------------------|
| Python | Python 3.7+ through 3.14 |
| Python | Python 3.8 through 3.14 |

> [!NOTE]
> This SDK works across all major operating platforms (Linux, macOS, Windows) as long as Python 3.7+ is available.
> This SDK works across all major operating platforms (Linux, macOS, Windows) as long as Python 3.8 or greater is available.

> [!IMPORTANT]
> SDK versions < 2.0.0 are [deprecated](https://ably.com/docs/platform/deprecate/protocol-v1).
> The `ably` package (3.x and earlier) is superseded by `ably-pubsub-server`. See [Migrating from `ably` 3.x](#migrating-from-ably-3x).

---

Expand All @@ -48,36 +70,100 @@ The following platforms are supported:
To get started with your project, install the package:

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

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

Optional extras: `crypto` for channel encryption, `vcdiff` for delta decoding,
and `oldcrypto` for the legacy `pycrypto` backend.

```sh
pip install "ably-pubsub-server[crypto]"
```

---

## Usage

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.
Clients are created through the factory functions in `ably_pubsub.server`. They
take exactly the arguments the client constructors take, and return the same
clients.

### Realtime client

Connects to Ably's realtime messaging service, subscribes to a channel to
receive messages, and publishes a test message to that same channel.

```python
# Initialize Ably Realtime client
async with AblyRealtime('your-ably-api-key', client_id='me') as realtime_client:
# Wait for connection to be established
import asyncio
from ably_pubsub.server import create_realtime_client


async def main():
realtime_client = create_realtime_client(key='your-ably-api-key', client_id='me')
await realtime_client.connection.once_async('connected')
print('Connected to Ably')

# Get a reference to the 'test-channel' channel

channel = realtime_client.channels.get('test-channel')

# Subscribe to all messages published to this channel

def on_message(message):
print(f'Received message: {message.data}')

await channel.subscribe(on_message)

# Publish a test message to the channel
await channel.publish('test-event', 'hello world')

await realtime_client.close()

asyncio.run(main())
```

### HTTP client

For publishing, history, presence reads, stats and token issuing, with no
persistent connection:

```python
import asyncio
from ably_pubsub.server import create_http_client


async def main():
async with create_http_client(key='your-ably-api-key') as client:
channel = client.channels.get('test-channel')
await channel.publish('test-event', 'hello world')

asyncio.run(main())
```

### Synchronous HTTP client

For code that has no event loop. There is no synchronous realtime client.

```python
from ably_pubsub.server.sync import create_http_client

client = create_http_client(key='your-ably-api-key')
client.channels.get('test-channel').publish('test-event', 'hello world')
client.close()
```

---

## Migrating from `ably` 3.x

Version 4.0.0 moves the SDK to the `ably-pubsub-server` distribution and the
`ably_pubsub.server` import namespace. For most applications the change is
confined to the install line, the import, and the constructor call.
[UPDATING.md](./UPDATING.md) has the full mapping table and worked examples.

The `ably` package receives security and critical-bug fixes only for one year
from the 4.0.0 release, and is then end-of-life. The two packages can be
installed side by side while you migrate — they use different import packages.

---

## Releases

The [CHANGELOG.md](https://github.com/ably/ably-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 All @@ -93,8 +179,3 @@ Read the [CONTRIBUTING.md](./CONTRIBUTING.md) guidelines to contribute to Ably.
## Support, feedback, and troubleshooting

For help or technical support, visit Ably's [support page](https://ably.com/support) or [GitHub Issues](https://github.com/ably/ably-python/issues) for community-reported bugs and discussions.

### Full Realtime support unavailable

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).

158 changes: 158 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,163 @@
# Upgrade / Migration Guide

## Version 3.x (`ably`) to 4.0.0 (`ably-pubsub-server`)

> **Status: draft.** The public API naming is still under review; the class and
> function names in this section may change before the 4.0.0 GA release.

Version 4.0.0 splits the SDK into new distributions, following
[PDR-091b](https://ably.atlassian.net/wiki/spaces/product/pages/5362810886). The
`ably` package is superseded: it receives security and critical-bug fixes only
for one year from the 4.0.0 release date, and is then end-of-life.

### Why

Under MAU-based pricing the platform must classify every connection as
device-side or server-side. The new packages declare which side they are on
automatically, as part of the agent identifier they put on the wire. The old
`ably` constructors cannot: nothing in them says where the code runs, so once
MAU pricing is live they are rejected on MAU-enabled accounts because the
platform cannot classify them.

Python is a server-side language, so there is a single new public distribution,
`ably-pubsub-server`, whose factory functions are the only recommended entry
points. It is built on `ably-pubsub-core`, an internal distribution you should
never depend on directly. The objects the factories return are the same clients
as today — channels, presence, history, auth and error handling are unchanged.
For most applications the migration is confined to the install line, the import,
and the constructor call.

### Mapping

| 3.x (`ably`) | 4.0 (`ably-pubsub-server`) |
| --- | --- |
| `pip install ably` | `pip install ably-pubsub-server` |
| `pip install ably[crypto]` | `pip install ably-pubsub-server[crypto]` |
| `from ably import AblyRest` | `from ably_pubsub.server import create_http_client` |
| `AblyRest(key=...)` | `create_http_client(key=...)` |
| `from ably import AblyRealtime` | `from ably_pubsub.server import create_realtime_client` |
| `AblyRealtime(key=...)` | `create_realtime_client(key=...)` |
| `from ably.sync import AblyRestSync` | `from ably_pubsub.server.sync import create_http_client` |
| `AblyRestSync(key=...)` | `create_http_client(key=...)` (from `ably_pubsub.server.sync`) |
| `from ably import X` (any name exported by `ably`) | `from ably_pubsub.server import X` |
| `from ably.types.channeloptions import ChannelOptions` | `from ably_pubsub.server import ChannelOptions` |
| `from ably.util.crypto import CipherParams` | `from ably_pubsub.server import CipherParams` |

#### Deep imports

In 3.x many types could only be reached by importing the submodule they were
defined in. In 4.0 all of them are re-exported from `ably_pubsub.server`, so the
submodule path goes away entirely — **the flat import is the supported one**.
Nothing under `ably_pubsub.core` is public API.

| 3.x deep import | 4.0 |
| --- | --- |
| `from ably.types.message import Message, MessageAnnotations` | `from ably_pubsub.server import Message, MessageAnnotations` |
| `from ably.types.presence import Presence, PresenceMessage, PresenceAction` | `from ably_pubsub.server import Presence, PresenceMessage, PresenceAction` |
| `from ably.types.tokenrequest import TokenRequest` | `from ably_pubsub.server import TokenRequest` |
| `from ably.types.tokendetails import TokenDetails` | `from ably_pubsub.server import TokenDetails` |
| `from ably.types.channeldetails import ChannelDetails, ChannelStatus, ChannelOccupancy, ChannelMetrics` | `from ably_pubsub.server import ChannelDetails, ChannelStatus, ChannelOccupancy, ChannelMetrics` |
| `from ably.types.channelstate import ChannelState, ChannelStateChange` | `from ably_pubsub.server import ChannelState, ChannelStateChange` |
| `from ably.types.connectionstate import ConnectionState, ConnectionEvent, ConnectionStateChange` | `from ably_pubsub.server import ConnectionState, ConnectionEvent, ConnectionStateChange` |
| `from ably.types.stats import Stats` | `from ably_pubsub.server import Stats` |
| `from ably.http.paginatedresult import PaginatedResult, HttpPaginatedResponse` | `from ably_pubsub.server import PaginatedResult, HttpPaginatedResponse` |
| `from ably.rest.channel import Channel` | `from ably_pubsub.server import Channel` |
| `from ably.realtime.channel import RealtimeChannel` | `from ably_pubsub.server import RealtimeChannel` |
| `from ably.realtime.connection import Connection` | `from ably_pubsub.server import Connection` |
| `from ably.realtime.presence import RealtimePresence` | `from ably_pubsub.server import RealtimePresence` |

The full supported surface of `ably_pubsub.server`:

```
AblyAuthException, AblyException, AblyRealtime, AblyRest, AblyVCDiffDecoder,
Annotation, AnnotationAction, Auth, Capability, Channel, ChannelDetails,
ChannelMetrics, ChannelMode, ChannelOccupancy, ChannelOptions, ChannelState,
ChannelStateChange, ChannelStatus, CipherParams, Connection, ConnectionEvent,
ConnectionState, ConnectionStateChange, DeviceDetails, HttpPaginatedResponse,
IncompatibleClientIdException, Message, MessageAction, MessageAnnotations,
MessageOperation, MessageVersion, Options, PaginatedResult, Presence,
PresenceAction, PresenceMessage, PublishResult, Push, PushChannelSubscription,
RealtimeChannel, RealtimePresence, SERVER_AGENT_IDENTIFIER, Stats,
TokenDetails, TokenRequest, UpdateDeleteResult, VCDiffDecoder,
create_http_client, create_realtime_client
```

`ably_pubsub.server.sync` re-exports the same set minus the realtime types, with
the synchronous flavours under their `Sync` names: `AblyRestSync`, `AuthSync`,
`PushSync`, `ChannelSync`, `PaginatedResultSync` and
`HttpPaginatedResponseSync`. There is no synchronous realtime client, so
`AblyRealtime`, `RealtimeChannel`, `RealtimePresence`, `Connection` and the
channel/connection state types are not there.

Two names you may go looking for and not find, in either version:

- **`TokenParams` is not a class in this SDK.** Token params are plain
dictionaries, for example
`await auth.request_token(token_params={'ttl': 3600000, 'client_id': 'me'})`.
- **There is no separate `ErrorInfo` type.** `AblyException` is the equivalent
and carries `code` and `status_code`; it is exported from
`ably_pubsub.server`.

### Example

```python
# 3.x
from ably import AblyRest

client = AblyRest(key='your-api-key')

# 4.0
from ably_pubsub.server import create_http_client

client = create_http_client(key='your-api-key')
```

```python
# 3.x
from ably import AblyRealtime

client = AblyRealtime(key='your-api-key', client_id='me')

# 4.0
from ably_pubsub.server import create_realtime_client

client = create_realtime_client(key='your-api-key', client_id='me')
```

```python
# 3.x
from ably.sync import AblyRestSync

client = AblyRestSync(key='your-api-key')

# 4.0
from ably_pubsub.server.sync import create_http_client

client = create_http_client(key='your-api-key')
```

The factories take exactly the keyword arguments the old constructors took:
`create_http_client(key=None, token=None, token_details=None, **options)` and
`create_realtime_client(key=None, loop=None, **options)`, where `**options` is
the same client options as before. The only argument that behaves differently is
`agents`: your entries are preserved, but the package's own `ably-pubsub-server`
entry is always added, so the wire agent reads
`ably-pubsub-python/4.0.0 python/3.x ably-pubsub-server`.

### Packaging changes

- **Python floor.** `requires-python` is now `>=3.8` (it was `>=3.7`, though 3.7
was already untested). Supported versions are 3.8 through 3.14.
- **Extras.** The same extras exist under the new name:
`ably-pubsub-server[crypto]`, `ably-pubsub-server[vcdiff]` and
`ably-pubsub-server[oldcrypto]`.
- **Two distributions.** `ably-pubsub-server` depends on `ably-pubsub-core`
pinned to the exact same version; the two are always released in lockstep. Do
not install or import `ably-pubsub-core` directly.
- **Side-by-side install is safe.** `ably` and `ably-pubsub-server` use
different import packages (`ably` and `ably_pubsub`), so both can be installed
in one environment while you migrate.

## Version 2.x to 3.0.0

The 3.0.0 version of ably-python introduces several breaking changes to improve the realtime experience and align the API with the Ably specification. These include:
Expand Down
Loading
Loading