Skip to content
Open
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
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
2 changes: 1 addition & 1 deletion .claude/skills/uts-to-python/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ Established against the real proxy and the real sandbox while deriving
asks for a token through a client pointed at the session puts a request in front of
the waiting rule and an extra `http_request` in the log, which breaks every
assertion that counts requests exactly (`== 1` for RSC15l's 4xx test, `>= 2` for the
fallback ones). Build an inner `AblyRest(key=api_key, endpoint=SANDBOX_ENDPOINT)`,
fallback ones). Build an inner `create_http_client(key=api_key, endpoint=SANDBOX_ENDPOINT)`,
request the token through that, and close it.
- **`http_response` events carry no `path`.** They have `status` and `ruleMatched`
only, so "the injected response fired" is read off the response events **in order**
Expand Down
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/
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/**
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
30 changes: 27 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 Down
93 changes: 93 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,98 @@
# Upgrade / Migration Guide

## Version 3.x to 4.0.0

The package is now published to PyPI as `ably-pubsub-server` rather than `ably`, and
it installs into `ably.pubsub` rather than `ably`. Both `ably` and `ably.pubsub` are
[namespace packages](https://peps.python.org/pep-0420/) shared with the other
`ably-*` distributions, so neither exports anything of its own. The whole public
API is reached through `ably.pubsub.server`.

### Installation

Example 3.x:
```sh
pip install ably
```

Example 4.0.0:
```sh
pip install ably-pubsub-server
```

### Imports

Example 3.x code:
```python
from ably import AblyRealtime, AblyRest
```

Example 4.0.0 code:
```python
from ably.pubsub.server import create_http_client, create_realtime_client
```

The synchronous, HTTP-only flavour moves the same way:

Example 3.x code:
```python
from ably.sync import AblyRestSync
```

Example 4.0.0 code:
```python
from ably.pubsub.server.sync import create_http_client
```

Where 3.x left you reaching into submodules such as `ably.types.message` or
`ably.http.paginatedresult`, import from `ably.pubsub.server` instead. It is
the whole public API, and `ably.pubsub.server.sync` is its synchronous
counterpart. The packages beneath them are internal and free to move.
Comment thread
ttypic marked this conversation as resolved.

### Clients are built by factories

`AblyRest` and `AblyRealtime` are internal in 4.0.0 and raise `TypeError` if
constructed directly. Build clients through the factories instead, which take the same options
as keyword arguments. The API key is no longer accepted positionally, so pass it as `key=`:

Example 3.x code:
```python
rest = AblyRest(key='xxx')
realtime = AblyRealtime(key='xxx')
```

Example 4.0.0 code:
```python
pubsub_http_client = create_http_client(key='xxx')
pubsub_realtime_client = create_realtime_client(key='xxx')
```

### Type annotations

The client classes are internal, so annotate against the prototypes the
factories are declared to return. Each is exported from the same module as the
factory that returns it.

Example 3.x code:
```python
from ably import AblyRealtime, AblyRest
from ably.sync import AblyRestSync

async def publish(client: AblyRest, name: str) -> None: ...
async def subscribe(client: AblyRealtime, name: str) -> None: ...
def publish_blocking(client: AblyRestSync, name: str) -> None: ...
```

Example 4.0.0 code:
```python
from ably.pubsub.server import PubSubHttpClient, PubSubRealtimeClient
from ably.pubsub.server.sync import PubSubHttpClient as PubSubHttpClientSync

async def publish(client: PubSubHttpClient, name: str) -> None: ...
async def subscribe(client: PubSubRealtimeClient, name: str) -> None: ...
def publish_blocking(client: PubSubHttpClientSync, name: str) -> None: ...
```

## 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
24 changes: 0 additions & 24 deletions ably/__init__.py

This file was deleted.

File renamed without changes.
14 changes: 7 additions & 7 deletions ably/rest/annotations.py → ably/pubsub/http/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

import msgpack

from ably.http.paginatedresult import PaginatedResult, format_params
from ably.types.annotation import (
from ably.pubsub.request.paginatedresult import PaginatedResult, format_params
from ably.pubsub.types.annotation import (
Annotation,
AnnotationAction,
make_annotation_response_handler,
)
from ably.types.message import Message
from ably.types.options import Options
from ably.util.exceptions import AblyException
from ably.pubsub.types.message import Message
from ably.pubsub.types.options import Options
from ably.pubsub.util.exceptions import AblyException

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -89,7 +89,7 @@ def construct_validate_annotation(msg_or_serial, annotation: Annotation) -> Anno
)


class RestAnnotations:
class HttpAnnotations:
"""
Provides REST API methods for managing annotations on messages.
"""
Expand All @@ -98,7 +98,7 @@ class RestAnnotations:

def __init__(self, channel):
"""
Initialize RestAnnotations.
Initialize HttpAnnotations.

Args:
channel: The REST Channel this annotations instance belongs to
Expand Down
22 changes: 11 additions & 11 deletions ably/rest/auth.py → ably/pubsub/http/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
from datetime import timedelta
from typing import TYPE_CHECKING

from ably.types.options import Options
from ably.pubsub.types.options import Options

if TYPE_CHECKING:
from ably.realtime.realtime import AblyRealtime
from ably.rest.rest import AblyRest
from ably.pubsub.http.http import DefaultPubSubHttpClient
from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient

from ably.types.capability import Capability
from ably.types.tokendetails import TokenDetails
from ably.types.tokenrequest import TokenRequest
from ably.util.clock import select_clock
from ably.util.exceptions import AblyAuthException, AblyException, IncompatibleClientIdException
from ably.util.helper import extract_url_params
from ably.pubsub.types.capability import Capability
from ably.pubsub.types.tokendetails import TokenDetails
from ably.pubsub.types.tokenrequest import TokenRequest
from ably.pubsub.util.clock import select_clock
from ably.pubsub.util.exceptions import AblyAuthException, AblyException, IncompatibleClientIdException
from ably.pubsub.util.helper import extract_url_params

__all__ = ["Auth"]

Expand All @@ -29,7 +29,7 @@ class Method:
BASIC = "BASIC"
TOKEN = "TOKEN"

def __init__(self, ably: AblyRest | AblyRealtime, options: Options):
def __init__(self, ably: DefaultPubSubHttpClient | DefaultPubSubRealtimeClient, options: Options):
self.__ably = ably
self.__auth_options = options
self.__clock = select_clock(options)
Expand Down Expand Up @@ -409,7 +409,7 @@ async def token_request_from_auth_url(self, method: str, url: str, token_params,
# Use clean URL for the request
url = clean_url

from ably.http.http import Response
from ably.pubsub.request.http import Response
resp = await self.ably.http.request_external(
method=method, url=url, headers=headers, params=params, body=body)
response = Response(resp)
Expand Down
Loading
Loading