You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PDR-091 splits the Pub/Sub SDKs so that the package an application
installs names the side it runs on. Ship that for Python as two thin
additive distributions over the existing package, which takes on the
role of the shared core and is otherwise unchanged:
- ably-pubsub-server provides ably.pubsub.server, with
create_http_client(), create_realtime_client(), and a sync submodule
- ably-pubsub-device provides ably.pubsub.device, with create_client()
Both re-export the core's public surface and return its clients
unchanged, so behaviour is identical by construction. AblyRest and
AblyRealtime emit a DeprecationWarning naming their replacement; they
keep working and are not scheduled for removal.
ably.pubsub is a PEP 420 namespace directory so that two distributions
can each contribute a subpackage to it. The source stays in the shared
ably/ tree because ably is a regular package, so Python resolves
ably.pubsub only under the directory ably was imported from; each sdist
reaches up to collect its subtree, and must therefore be built before
its wheel.
All three distributions publish in a single upload, since the wrappers
pin the core exactly and a partial release is unusable. That requires
all three PyPI projects to register the same trusted publisher.
The side-declaring agent value PDR-091 also calls for is not included
here: the packages name the side, but nothing distinguishes them on the
wire yet.
Tests cover factory pass-through and warning suppression, the
deprecation in both async and generated sync flavours, re-export parity
with the core, and the packaging invariants (namespace directory,
version lockstep, exact core pin).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+31-2Lines changed: 31 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,12 +15,39 @@ git submodule update
15
15
uv sync --extra crypto
16
16
```
17
17
18
+
### Repository layout
19
+
20
+
This repository builds three distributions, released together on the same version. They all install into the one `ably` package, so what you import never tells you which distribution shipped it:
21
+
22
+
| Distribution | Source | Imported as | Role |
23
+
|--------------|--------|-------------|------|
24
+
|`ably`|[`ably/`](./ably), except `ably/pubsub`|`ably`, `ably.sync`| The shared core, containing all of the implementation |
25
+
|`ably-pubsub-server`|[`ably/pubsub/server/`](./ably/pubsub/server)|`ably.pubsub.server`| The server-side factories |
26
+
|`ably-pubsub-device`|[`ably/pubsub/device/`](./ably/pubsub/device)|`ably.pubsub.device`| The device-side factory |
27
+
28
+
Each side re-exports the core's public surface and adds factories that return the core's clients unchanged, so that the package a caller installs names the side their application runs on. They pin the core exactly, so any change to the core's public surface needs the corresponding re-export added to both.
29
+
30
+
The packaging metadata for the two pubsub distributions lives in [`packages/`](./packages), away from the code it ships. Two rules keep that arrangement working, and both are covered by [`test/unit/pubsub_packaging_test.py`](./test/unit/pubsub_packaging_test.py):
31
+
32
+
-**`ably/pubsub/` must not gain an `__init__.py`.** It is a namespace directory (PEP 420) so that two distributions can each contribute a subpackage to it. An `__init__.py` would belong to whichever one shipped it, and removing that distribution would take the other side's subpackage with it.
33
+
-**The source stays in the shared `ably/` tree**, not beside the `pyproject.toml` that ships it. `ably` is a regular package, so Python looks for `ably.pubsub` only under the directory `ably` itself was imported from — in a checkout, that is `ably/`. Each sdist reaches up to collect its subtree, and its wheel is then built from that sdist.
34
+
18
35
### Running the test suite
19
36
20
37
```shell
21
38
uv run pytest
22
39
```
23
40
41
+
Because the pubsub code lives in the `ably/` tree, `ably.pubsub.server` and `ably.pubsub.device` import from a checkout with nothing installed beyond the core. Their tests are in [`test/unit/`](./test/unit) and need no network.
42
+
43
+
To build all three distributions — build the sdist first, which `uv build` does by default:
1. Ensure that all work intended for this release has landed to `main`
@@ -36,11 +63,13 @@ uv run pytest
36
63
37
64
Releases should always be made through a release pull request (PR), which needs to bump the version number and add to the [change log](CHANGELOG.md).
38
65
66
+
`ably`, `ably-pubsub-server` and `ably-pubsub-device` are published in a single upload, so that a release is all three or none of them — the wrappers pin the core exactly, so a partial release is an unusable one. This works because the short-lived token PyPI mints from an OIDC request carries every project that trusts the requesting configuration, which means **all three PyPI projects must register the same trusted publisher**: this repository, `release.yml`, and the `pypi` environment (and likewise `testpypi`). Adding a fourth distribution means registering it the same way before its first release, or the whole upload fails.
67
+
39
68
The release process must include the following steps:
40
69
41
70
1. Ensure that all work intended for this release has landed to `main`
42
71
2. Create a release branch named like `release/2.0.1`
43
-
3. Add a commit to bump the version number, updating [`pyproject.toml`](./pyproject.toml) and [`ably/__init__.py`](./ably/__init__.py)
72
+
3. Add a commit to bump the version number. All three distributions release in lockstep, so this means [`pyproject.toml`](./pyproject.toml), [`ably/__init__.py`](./ably/__init__.py), and, for each pubsub distribution, its `pyproject.toml` under [`packages/`](./packages) (both its own version and its `ably==` pins) and the `__version__` in its module under [`ably/pubsub/`](./ably/pubsub). The tests in [`test/unit/pubsub_packaging_test.py`](./test/unit/pubsub_packaging_test.py) fail if any of these drift apart
44
73
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:
45
74
- The command you will need to run will look something like this: `github_changelog_generator -u ably -p ably-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).
46
75
- Using the command above, `--output delta.md` writes changes made after `--since-tag` to a new file
@@ -51,7 +80,7 @@ The release process must include the following steps:
51
80
7. Create a release PR (ensure you include an SDK Team Engineering Lead and the SDK Team Product Manager as reviewers) and gain approvals for it, then merge that to `main`
52
81
8. Create a tag named like `v2.0.1` and push it to GitHub - e.g. `git tag v2.0.1 && git push origin v2.0.1`
53
82
9. Create the release on GitHub including populating the release notes
54
-
10. Go to the [Release Workflow](https://github.com/ably/ably-python/actions/workflows/release.yml) and ask [ably/team-sdk](https://github.com/orgs/ably/teams/team-sdk) member to approve publishing to the PyPI registry
83
+
10. Go to the [Release Workflow](https://github.com/ably/ably-python/actions/workflows/release.yml) and ask [ably/team-sdk](https://github.com/orgs/ably/teams/team-sdk) member to approve publishing to the PyPI registry. All three distributions go up in a single upload, so there is one approval for the release as a whole
55
84
11. Update the [Ably Changelog](https://changelog.ably.com/) (via [headwayapp](https://headwayapp.co/)) with these changes
56
85
57
86
We tend to use [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator) to collate the information required for a change log update.
Installing `ably` on its own also still works, and remains fully supported. It is the shared core both build on, and the clients they return are its clients unchanged.
59
+
54
60
> [!NOTE]
55
61
Install [Python](https://www.python.org/downloads/) version 3.8 or greater.
56
62
@@ -59,8 +65,10 @@ Install [Python](https://www.python.org/downloads/) version 3.8 or greater.
59
65
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.
60
66
61
67
```python
68
+
from ably.pubsub.device import create_client
69
+
62
70
# Initialize Ably Realtime client
63
-
asyncwithAblyRealtime('your-ably-api-key', client_id='me') as realtime_client:
71
+
asyncwithcreate_client('your-ably-api-key', client_id='me') as realtime_client:
On a server, use `ably.pubsub.server.create_realtime_client()` for the same client over a persistent connection, or `ably.pubsub.server.create_http_client()` when publish, history, presence reads, stats and token issuing over HTTP are enough. A synchronous HTTP client is available from `ably.pubsub.server.sync`.
90
+
91
+
### Migrating from the AblyRest and AblyRealtime constructors
92
+
93
+
Constructing `ably.AblyRest` or `ably.AblyRealtime` directly still works and is not scheduled for removal, but it emits a `DeprecationWarning` pointing at the factory for your side:
94
+
95
+
| Before | After |
96
+
|--------|-------|
97
+
|`ably.AblyRealtime(...)` on a device |`ably.pubsub.device.create_client(...)`|
98
+
|`ably.AblyRealtime(...)` on a server |`ably.pubsub.server.create_realtime_client(...)`|
The factories take the same arguments as the constructors they replace and behave identically to them, so migrating is a change of entry point only.
103
+
81
104
## Releases
82
105
83
106
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).
0 commit comments