diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 01539b45..985df013 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -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: diff --git a/.claude/skills/uts-to-python/SKILL.md b/.claude/skills/uts-to-python/SKILL.md index a413d6bb..5a93bf7a 100644 --- a/.claude/skills/uts-to-python/SKILL.md +++ b/.claude/skills/uts-to-python/SKILL.md @@ -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** diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a7a2ae2..f40e27e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/.gitignore b/.gitignore index 75ec0f34..426ac840 100644 --- a/.gitignore +++ b/.gitignore @@ -55,5 +55,5 @@ ably/types/options.py.orig test/ably/restsetup.py.orig .idea/**/* -ably/sync/** +ably/pubsub/sync/** test/ably/sync/** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed650adb..b75ffbaa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/LONG_DESCRIPTION.rst b/LONG_DESCRIPTION.rst index 518f2f86..54f7289c 100644 --- a/LONG_DESCRIPTION.rst +++ b/LONG_DESCRIPTION.rst @@ -1,7 +1,8 @@ -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 @@ -9,7 +10,13 @@ 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 diff --git a/README.md b/README.md index 5bfb89e0..e833df38 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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. @@ -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') diff --git a/UPDATING.md b/UPDATING.md index 4b4dd719..a2aa7db7 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -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. + +### 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: diff --git a/ably/__init__.py b/ably/__init__.py deleted file mode 100644 index 95412484..00000000 --- a/ably/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -import logging - -from ably.realtime.realtime import AblyRealtime -from ably.rest.auth import Auth -from ably.rest.push import Push -from ably.rest.rest import AblyRest -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.capability import Capability -from ably.types.channelmode import ChannelMode -from ably.types.channeloptions import ChannelOptions -from ably.types.channelsubscription import PushChannelSubscription -from ably.types.device import DeviceDetails -from ably.types.message import MessageAction, MessageVersion -from ably.types.operations import MessageOperation, PublishResult, UpdateDeleteResult -from ably.types.options import Options, VCDiffDecoder -from ably.util.crypto import CipherParams -from ably.util.exceptions import AblyAuthException, AblyException, IncompatibleClientIdException -from ably.vcdiff.defaultvcdiffdecoder import AblyVCDiffDecoder - -logger = logging.getLogger(__name__) -logger.addHandler(logging.NullHandler()) - -api_version = '5' -lib_version = '3.1.4' diff --git a/ably/http/__init__.py b/ably/pubsub/http/__init__.py similarity index 100% rename from ably/http/__init__.py rename to ably/pubsub/http/__init__.py diff --git a/ably/rest/annotations.py b/ably/pubsub/http/annotations.py similarity index 95% rename from ably/rest/annotations.py rename to ably/pubsub/http/annotations.py index 39c1664d..88332352 100644 --- a/ably/rest/annotations.py +++ b/ably/pubsub/http/annotations.py @@ -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__) @@ -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. """ @@ -98,7 +98,7 @@ class RestAnnotations: def __init__(self, channel): """ - Initialize RestAnnotations. + Initialize HttpAnnotations. Args: channel: The REST Channel this annotations instance belongs to diff --git a/ably/rest/auth.py b/ably/pubsub/http/auth.py similarity index 96% rename from ably/rest/auth.py rename to ably/pubsub/http/auth.py index fe2aeae3..aa71d517 100644 --- a/ably/rest/auth.py +++ b/ably/pubsub/http/auth.py @@ -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"] @@ -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) @@ -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) diff --git a/ably/rest/channel.py b/ably/pubsub/http/channel.py similarity index 96% rename from ably/rest/channel.py rename to ably/pubsub/http/channel.py index 7603a224..531a17cd 100644 --- a/ably/rest/channel.py +++ b/ably/pubsub/http/channel.py @@ -8,20 +8,20 @@ import msgpack -from ably.http.paginatedresult import PaginatedResult, format_params -from ably.rest.annotations import RestAnnotations -from ably.types.channeldetails import ChannelDetails -from ably.types.message import ( +from ably.pubsub.http.annotations import HttpAnnotations +from ably.pubsub.request.paginatedresult import PaginatedResult, format_params +from ably.pubsub.types.channeldetails import ChannelDetails +from ably.pubsub.types.message import ( Message, MessageAction, MessageVersion, make_message_response_handler, make_single_message_response_handler, ) -from ably.types.operations import MessageOperation, PublishResult, UpdateDeleteResult -from ably.types.presence import Presence -from ably.util.crypto import get_cipher -from ably.util.exceptions import ( +from ably.pubsub.types.operations import MessageOperation, PublishResult, UpdateDeleteResult +from ably.pubsub.types.presence import Presence +from ably.pubsub.util.crypto import get_cipher +from ably.pubsub.util.exceptions import ( AblyException, IncompatibleClientIdException, catch_all, @@ -31,7 +31,7 @@ class Channel: - __annotations: RestAnnotations + __annotations: HttpAnnotations def __init__(self, ably, name, options): self.__ably = ably @@ -40,7 +40,7 @@ def __init__(self, ably, name, options): self.__cipher = None self.options = options self.__presence = Presence(self) - self.__annotations = RestAnnotations(self) + self.__annotations = HttpAnnotations(self) @catch_all async def history(self, direction=None, limit: int = None, start=None, end=None): @@ -370,7 +370,7 @@ def presence(self): return self.__presence @property - def annotations(self) -> RestAnnotations: + def annotations(self) -> HttpAnnotations: return self.__annotations @options.setter diff --git a/ably/rest/rest.py b/ably/pubsub/http/http.py similarity index 83% rename from ably/rest/rest.py rename to ably/pubsub/http/http.py index bc84e638..457c0635 100644 --- a/ably/rest/rest.py +++ b/ably/pubsub/http/http.py @@ -2,25 +2,31 @@ from typing import Optional from urllib.parse import urlencode -from ably.http.http import Http -from ably.http.paginatedresult import HttpPaginatedResponse, PaginatedResult, format_params -from ably.rest.auth import Auth -from ably.rest.channel import Channels -from ably.rest.push import Push -from ably.types.options import Options -from ably.types.stats import stats_response_processor -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException, catch_all +from ably.pubsub.http.auth import Auth +from ably.pubsub.http.channel import Channels +from ably.pubsub.http.push import Push +from ably.pubsub.prototypes import PubSubHttpClient +from ably.pubsub.request.http import Http +from ably.pubsub.request.paginatedresult import HttpPaginatedResponse, PaginatedResult, format_params +from ably.pubsub.types.options import Options +from ably.pubsub.types.stats import stats_response_processor +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.construction import reject_direct_construction +from ably.pubsub.util.exceptions import AblyException, catch_all log = logging.getLogger(__name__) -class AblyRest: - """Ably Rest Client""" +class DefaultPubSubHttpClient(PubSubHttpClient): + """The default :class:`~ably.pubsub.prototypes.PubSubHttpClient` implementation. + + Built by :func:`ably.pubsub.server.create_http_client`, which is the only + way to construct one. + """ def __init__(self, key: Optional[str] = None, token: Optional[str] = None, token_details: Optional[TokenDetails] = None, **kwargs): - """Create an AblyRest instance. + """Create an DefaultPubSubHttpClient instance. :Parameters: **Credentials** @@ -47,8 +53,9 @@ def __init__(self, key: Optional[str] = None, token: Optional[str] = None, - `auth_token`: Undocumented - `auth_callback`: Undocumented - `auth_url`: Undocumented - - `keep_alive`: use persistent connections. Defaults to True """ + reject_direct_construction(type(self), 'ably.pubsub.server.create_http_client') + if key is not None and ('key_name' in kwargs or 'key_secret' in kwargs): raise ValueError("key and key_name or key_secret are mutually exclusive. " "Provider either a key or key_name & key_secret") diff --git a/ably/rest/push.py b/ably/pubsub/http/push.py similarity index 96% rename from ably/rest/push.py rename to ably/pubsub/http/push.py index f99b2b1d..afcea029 100644 --- a/ably/rest/push.py +++ b/ably/pubsub/http/push.py @@ -1,12 +1,12 @@ from typing import Optional -from ably.http.paginatedresult import PaginatedResult, format_params -from ably.types.channelsubscription import ( +from ably.pubsub.request.paginatedresult import PaginatedResult, format_params +from ably.pubsub.types.channelsubscription import ( PushChannelSubscription, channel_subscriptions_response_processor, channels_response_processor, ) -from ably.types.device import DeviceDetails, device_details_response_processor +from ably.pubsub.types.device import DeviceDetails, device_details_response_processor class Push: diff --git a/ably/pubsub/prototypes.py b/ably/pubsub/prototypes.py new file mode 100644 index 00000000..f269ab7f --- /dev/null +++ b/ably/pubsub/prototypes.py @@ -0,0 +1,121 @@ +"""The shapes of the Pub/Sub clients, as callers see them. + +The client classes themselves are internal: they are reachable only through the +factory functions in :mod:`ably.pubsub.server` and :mod:`ably.pubsub.server.sync`, +so that the package a client comes from names the side the application runs on. +These protocols are what those factories are declared to return, and what +application code should annotate against. + +The clients name their prototype as a base class rather than satisfying it +structurally, so the relationship is stated where the client is defined and a +type checker verifies it. One consequence to know about: an unimplemented +member resolves to the stub below and returns None instead of raising, so +``test/unit/pubsub_server_test.py`` checks that each client defines every +member itself, and with the signature declared here. + +They are named "prototypes" rather than "protocols" because this codebase +already uses "protocol" for the Ably wire protocol. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Protocol + +if TYPE_CHECKING: + from ably.pubsub.http.auth import Auth + from ably.pubsub.http.channel import Channels as HttpChannels + from ably.pubsub.http.push import Push + from ably.pubsub.realtime.channel import Channels as RealtimeChannels + from ably.pubsub.realtime.connection import Connection + from ably.pubsub.request.paginatedresult import HttpPaginatedResponse, PaginatedResult + from ably.pubsub.types.options import Options + + +class PubSubHttpClient(Protocol): + """A Pub/Sub client that operates entirely over HTTP. + + Build one with ``create_http_client``, from :mod:`ably.pubsub.server` for the + asynchronous flavour or :mod:`ably.pubsub.server.sync` for the synchronous one. + """ + + @property + def auth(self) -> Auth: + """The authentication object for this client.""" + ... + + @property + def channels(self) -> HttpChannels: + """The channels container object.""" + ... + + @property + def client_id(self) -> str | None: + """The client ID this client authenticates as, if any.""" + ... + + @property + def options(self) -> Options: + """The client options this client was built with.""" + ... + + @property + def push(self) -> Push: + """The push notification admin object.""" + ... + + async def stats(self, direction: str | None = None, start=None, end=None, params: dict | None = None, + limit: int | None = None, paginated=None, unit=None, timeout=None) -> PaginatedResult: + """Return the stats for this application.""" + ... + + async def time(self, timeout: float | None = None) -> float: + """Return the current server time in ms since the unix epoch.""" + ... + + async def request(self, method: str, path: str, version: str, params: dict | None = None, + body=None, headers=None) -> HttpPaginatedResponse: + """Make an arbitrary request against the Ably REST API.""" + ... + + async def close(self) -> None: + """Release the resources this client holds.""" + ... + + async def __aenter__(self) -> PubSubHttpClient: + ... + + async def __aexit__(self, *excinfo) -> None: + ... + + +class PubSubRealtimeClient(PubSubHttpClient, Protocol): + """A Pub/Sub client with a persistent realtime connection. + + Everything :class:`PubSubHttpClient` does, plus subscribing to channels and + entering presence. Build one with + :func:`ably.pubsub.server.create_realtime_client`. + """ + + @property + def channels(self) -> RealtimeChannels: + """The realtime channels container object.""" + ... + + @property + def connection(self) -> Connection: + """The realtime connection object.""" + ... + + def connect(self) -> None: + """Establish the realtime connection. + + Unnecessary unless the client was built with ``auto_connect=False``. + """ + ... + + async def close(self) -> None: + """Close the realtime connection and release the client's resources.""" + ... + + async def __aenter__(self) -> PubSubRealtimeClient: + ... diff --git a/ably/realtime/__init__.py b/ably/pubsub/realtime/__init__.py similarity index 100% rename from ably/realtime/__init__.py rename to ably/pubsub/realtime/__init__.py diff --git a/ably/realtime/annotations.py b/ably/pubsub/realtime/annotations.py similarity index 92% rename from ably/realtime/annotations.py rename to ably/pubsub/realtime/annotations.py index 57400d45..cf83c609 100644 --- a/ably/realtime/annotations.py +++ b/ably/pubsub/realtime/annotations.py @@ -3,17 +3,17 @@ import logging from typing import TYPE_CHECKING -from ably.rest.annotations import RestAnnotations, construct_validate_annotation -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.channelmode import ChannelMode -from ably.types.channelstate import ChannelState -from ably.util.eventemitter import EventEmitter -from ably.util.helper import is_callable_or_coroutine +from ably.pubsub.http.annotations import HttpAnnotations, construct_validate_annotation +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.helper import is_callable_or_coroutine if TYPE_CHECKING: - from ably.realtime.channel import RealtimeChannel - from ably.realtime.connectionmanager import ConnectionManager + from ably.pubsub.realtime.channel import RealtimeChannel + from ably.pubsub.realtime.connectionmanager import ConnectionManager log = logging.getLogger(__name__) @@ -37,7 +37,7 @@ def __init__(self, channel: RealtimeChannel, connection_manager: ConnectionManag self.__channel = channel self.__connection_manager = connection_manager self.__subscriptions = EventEmitter() - self.__rest_annotations = RestAnnotations(channel) + self.__http_annotations = HttpAnnotations(channel) async def __send_annotation(self, annotation: Annotation, params: dict | None = None): """ @@ -264,4 +264,4 @@ async def get(self, msg_or_serial, params: dict | None = None): AblyException: If the request fails or serial is invalid """ # Delegate to REST implementation - return await self.__rest_annotations.get(msg_or_serial, params) + return await self.__http_annotations.get(msg_or_serial, params) diff --git a/ably/realtime/channel.py b/ably/pubsub/realtime/channel.py similarity index 96% rename from ably/realtime/channel.py rename to ably/pubsub/realtime/channel.py index fde9bb06..0dfd720b 100644 --- a/ably/realtime/channel.py +++ b/ably/pubsub/realtime/channel.py @@ -4,28 +4,28 @@ import logging from typing import TYPE_CHECKING -from ably.realtime.annotations import RealtimeAnnotations -from ably.realtime.connection import ConnectionState -from ably.realtime.presence import RealtimePresence -from ably.rest.channel import Channel -from ably.rest.channel import Channels as RestChannels -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.annotation import Annotation -from ably.types.channelmode import ChannelMode, decode_channel_mode, encode_channel_mode -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState, ChannelStateChange -from ably.types.flags import Flag, has_flag -from ably.types.message import Message, MessageAction, MessageVersion -from ably.types.mixins import DecodingContext -from ably.types.operations import MessageOperation, PublishResult, UpdateDeleteResult -from ably.types.presence import PresenceMessage -from ably.util.clock import select_clock -from ably.util.eventemitter import EventEmitter -from ably.util.exceptions import AblyException, IncompatibleClientIdException -from ably.util.helper import Timer, is_callable_or_coroutine, validate_message_size +from ably.pubsub.http.channel import Channel +from ably.pubsub.http.channel import Channels as HttpChannels +from ably.pubsub.realtime.annotations import RealtimeAnnotations +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.realtime.presence import RealtimePresence +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.annotation import Annotation +from ably.pubsub.types.channelmode import ChannelMode, decode_channel_mode, encode_channel_mode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState, ChannelStateChange +from ably.pubsub.types.flags import Flag, has_flag +from ably.pubsub.types.message import Message, MessageAction, MessageVersion +from ably.pubsub.types.mixins import DecodingContext +from ably.pubsub.types.operations import MessageOperation, PublishResult, UpdateDeleteResult +from ably.pubsub.types.presence import PresenceMessage +from ably.pubsub.util.clock import select_clock +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.exceptions import AblyException, IncompatibleClientIdException +from ably.pubsub.util.helper import Timer, is_callable_or_coroutine, validate_message_size if TYPE_CHECKING: - from ably.realtime.realtime import AblyRealtime + from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient log = logging.getLogger(__name__) @@ -55,7 +55,8 @@ class RealtimeChannel(EventEmitter, Channel): Unsubscribe to messages from a channel """ - def __init__(self, realtime: AblyRealtime, name: str, channel_options: ChannelOptions | None = None): + def __init__(self, realtime: DefaultPubSubRealtimeClient, name: str, + channel_options: ChannelOptions | None = None): EventEmitter.__init__(self) self.__name = name self.__realtime = realtime @@ -409,7 +410,7 @@ async def publish(self, *args, **kwargs) -> PublishResult: 400, 40012) - # Encode messages (RTL6a: same encoding as RestChannel#publish) + # Encode messages (RTL6a: same encoding as HttpChannel#publish) encoded_messages = [] for m in messages: # Encode the message with encryption if needed @@ -962,7 +963,7 @@ def _encode_flags(self) -> int | None: return flags -class Channels(RestChannels): +class Channels(HttpChannels): """Creates and destroys RealtimeChannel objects. Methods diff --git a/ably/realtime/connection.py b/ably/pubsub/realtime/connection.py similarity index 89% rename from ably/realtime/connection.py rename to ably/pubsub/realtime/connection.py index 907f56a5..fb7854df 100644 --- a/ably/realtime/connection.py +++ b/ably/pubsub/realtime/connection.py @@ -5,14 +5,14 @@ import logging from typing import TYPE_CHECKING -from ably.realtime.connectionmanager import ConnectionManager -from ably.types.connectiondetails import ConnectionDetails -from ably.types.connectionstate import ConnectionEvent, ConnectionState, ConnectionStateChange -from ably.util.eventemitter import EventEmitter -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connectionmanager import ConnectionManager +from ably.pubsub.types.connectiondetails import ConnectionDetails +from ably.pubsub.types.connectionstate import ConnectionEvent, ConnectionState, ConnectionStateChange +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.exceptions import AblyException if TYPE_CHECKING: - from ably.realtime.realtime import AblyRealtime + from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient log = logging.getLogger(__name__) @@ -40,7 +40,7 @@ class Connection(EventEmitter): # RTN4 Pings a realtime connection """ - def __init__(self, realtime: AblyRealtime): + def __init__(self, realtime: DefaultPubSubRealtimeClient): self.__realtime = realtime self.__error_reason: AblyException | None = None self.__state = ConnectionState.CONNECTING if realtime.options.auto_connect else ConnectionState.INITIALIZED diff --git a/ably/realtime/connectionmanager.py b/ably/pubsub/realtime/connectionmanager.py similarity index 97% rename from ably/realtime/connectionmanager.py rename to ably/pubsub/realtime/connectionmanager.py index 9f920b07..09ce5eab 100644 --- a/ably/realtime/connectionmanager.py +++ b/ably/pubsub/realtime/connectionmanager.py @@ -8,20 +8,20 @@ import httpx -from ably.transport.defaults import Defaults -from ably.transport.websockettransport import ProtocolMessageAction, WebSocketTransport -from ably.types.connectiondetails import ConnectionDetails -from ably.types.connectionerrors import ConnectionErrors -from ably.types.connectionstate import ConnectionEvent, ConnectionState, ConnectionStateChange -from ably.types.operations import PublishResult -from ably.types.tokendetails import TokenDetails -from ably.util.clock import Clock, select_clock -from ably.util.eventemitter import EventEmitter -from ably.util.exceptions import AblyException, IncompatibleClientIdException -from ably.util.helper import Timer, get_random_id, is_token_error +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.transport.websockettransport import ProtocolMessageAction, WebSocketTransport +from ably.pubsub.types.connectiondetails import ConnectionDetails +from ably.pubsub.types.connectionerrors import ConnectionErrors +from ably.pubsub.types.connectionstate import ConnectionEvent, ConnectionState, ConnectionStateChange +from ably.pubsub.types.operations import PublishResult +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.clock import Clock, select_clock +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.exceptions import AblyException, IncompatibleClientIdException +from ably.pubsub.util.helper import Timer, get_random_id, is_token_error if TYPE_CHECKING: - from ably.realtime.realtime import AblyRealtime + from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient log = logging.getLogger(__name__) @@ -139,7 +139,7 @@ def response_time_ms(self) -> float: class ConnectionManager(EventEmitter): - def __init__(self, realtime: AblyRealtime, initial_state): + def __init__(self, realtime: DefaultPubSubRealtimeClient, initial_state): self.options = realtime.options self.clock = select_clock(self.options) self.__ably = realtime diff --git a/ably/realtime/presence.py b/ably/pubsub/realtime/presence.py similarity index 98% rename from ably/realtime/presence.py rename to ably/pubsub/realtime/presence.py index 79d73070..60b3b211 100644 --- a/ably/realtime/presence.py +++ b/ably/pubsub/realtime/presence.py @@ -12,15 +12,15 @@ from datetime import datetime, timezone from typing import TYPE_CHECKING, Any -from ably.realtime.connection import ConnectionState -from ably.realtime.presencemap import PresenceMap -from ably.types.channelstate import ChannelState, ChannelStateChange -from ably.types.presence import PresenceAction, PresenceMessage -from ably.util.eventemitter import EventEmitter -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.realtime.presencemap import PresenceMap +from ably.pubsub.types.channelstate import ChannelState, ChannelStateChange +from ably.pubsub.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.exceptions import AblyException if TYPE_CHECKING: - from ably.realtime.channel import RealtimeChannel + from ably.pubsub.realtime.channel import RealtimeChannel log = logging.getLogger(__name__) @@ -354,7 +354,7 @@ async def _send_presence(self, presence_messages: list[dict]) -> None: Args: presence_messages: List of encoded presence messages to send """ - from ably.transport.websockettransport import ProtocolMessageAction + from ably.pubsub.transport.websockettransport import ProtocolMessageAction protocol_msg = { 'action': ProtocolMessageAction.PRESENCE, diff --git a/ably/realtime/presencemap.py b/ably/pubsub/realtime/presencemap.py similarity index 99% rename from ably/realtime/presencemap.py rename to ably/pubsub/realtime/presencemap.py index 9c5adace..a5d2c427 100644 --- a/ably/realtime/presencemap.py +++ b/ably/pubsub/realtime/presencemap.py @@ -7,7 +7,7 @@ import logging from typing import Callable, Dict, List, Optional, Tuple -from ably.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.types.presence import PresenceAction, PresenceMessage logger = logging.getLogger(__name__) diff --git a/ably/realtime/realtime.py b/ably/pubsub/realtime/realtime.py similarity index 88% rename from ably/realtime/realtime.py rename to ably/pubsub/realtime/realtime.py index ab435304..36b1118d 100644 --- a/ably/realtime/realtime.py +++ b/ably/pubsub/realtime/realtime.py @@ -2,16 +2,21 @@ import logging from typing import Optional -from ably.realtime.channel import Channels -from ably.realtime.connection import Connection, ConnectionState -from ably.rest.rest import AblyRest +from ably.pubsub.http.http import DefaultPubSubHttpClient +from ably.pubsub.prototypes import PubSubRealtimeClient +from ably.pubsub.realtime.channel import Channels +from ably.pubsub.realtime.connection import Connection, ConnectionState +from ably.pubsub.util.construction import reject_direct_construction log = logging.getLogger(__name__) -class AblyRealtime(AblyRest): +class DefaultPubSubRealtimeClient(DefaultPubSubHttpClient, PubSubRealtimeClient): """ - Ably Realtime Client + The default :class:`~ably.pubsub.prototypes.PubSubRealtimeClient` implementation. + + Built by :func:`ably.pubsub.server.create_realtime_client`, which is the + only way to construct one. Attributes ---------- @@ -35,7 +40,7 @@ class AblyRealtime(AblyRest): """ def __init__(self, key: Optional[str] = None, loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs): - """Constructs a RealtimeClient object using an Ably API key. + """Constructs a PubSubRealtimeClient object using an Ably API key. Parameters ---------- @@ -88,6 +93,7 @@ def __init__(self, key: Optional[str] = None, loop: Optional[asyncio.AbstractEve ValueError If no authentication key is not provided """ + reject_direct_construction(type(self), 'ably.pubsub.server.create_realtime_client') if loop is None: try: diff --git a/ably/rest/__init__.py b/ably/pubsub/request/__init__.py similarity index 100% rename from ably/rest/__init__.py rename to ably/pubsub/request/__init__.py diff --git a/ably/http/http.py b/ably/pubsub/request/http.py similarity index 97% rename from ably/http/http.py rename to ably/pubsub/request/http.py index 6ffd6d36..c664f1ab 100644 --- a/ably/http/http.py +++ b/ably/pubsub/request/http.py @@ -6,12 +6,12 @@ import httpx import msgpack -from ably.http.httputils import HttpUtils -from ably.rest.auth import Auth -from ably.transport.defaults import Defaults -from ably.util.clock import select_clock -from ably.util.exceptions import AblyException -from ably.util.helper import extract_url_params, is_token_error +from ably.pubsub.http.auth import Auth +from ably.pubsub.request.httputils import HttpUtils +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.util.clock import select_clock +from ably.pubsub.util.exceptions import AblyException +from ably.pubsub.util.helper import extract_url_params, is_token_error log = logging.getLogger(__name__) diff --git a/ably/http/httputils.py b/ably/pubsub/request/httputils.py similarity index 90% rename from ably/http/httputils.py rename to ably/pubsub/request/httputils.py index 4bd3e78e..f24a2f00 100644 --- a/ably/http/httputils.py +++ b/ably/pubsub/request/httputils.py @@ -3,7 +3,7 @@ import os import platform -import ably +from ably.pubsub.version import api_version, lib_version class HttpUtils: @@ -53,10 +53,10 @@ def get_host_header(host): @staticmethod def default_headers(version=None): if version is None: - version = ably.api_version + version = api_version return { "X-Ably-Version": version, - "Ably-Agent": f'ably-python/{ably.lib_version} python/{platform.python_version()}' + "Ably-Agent": f'ably-pubsub-python/{lib_version} python/{platform.python_version()}' } @staticmethod diff --git a/ably/http/paginatedresult.py b/ably/pubsub/request/paginatedresult.py similarity index 98% rename from ably/http/paginatedresult.py rename to ably/pubsub/request/paginatedresult.py index 5b8bc158..d3361ee1 100644 --- a/ably/http/paginatedresult.py +++ b/ably/pubsub/request/paginatedresult.py @@ -2,8 +2,8 @@ import logging from urllib.parse import urlencode -from ably.http.http import Request -from ably.util import case +from ably.pubsub.request.http import Request +from ably.pubsub.util import case log = logging.getLogger(__name__) diff --git a/ably/scripts/__init__.py b/ably/pubsub/scripts/__init__.py similarity index 100% rename from ably/scripts/__init__.py rename to ably/pubsub/scripts/__init__.py diff --git a/ably/scripts/unasync.py b/ably/pubsub/scripts/unasync.py similarity index 84% rename from ably/scripts/unasync.py rename to ably/pubsub/scripts/unasync.py index d13e20f2..4f6c8f16 100644 --- a/ably/scripts/unasync.py +++ b/ably/pubsub/scripts/unasync.py @@ -5,7 +5,7 @@ import tokenize_rt rename_classes = [ - "AblyRest", + "DefaultPubSubHttpClient", "Push", "PushAdmin", "Channel", @@ -241,19 +241,32 @@ def run(): _TOKEN_REPLACE["aclose"] = "close" _TOKEN_REPLACE["assert_waiter"] = "assert_waiter_sync" - _IMPORTS_REPLACE["ably"] = "ably.sync" + # Ordering matters: _replace_import takes the first key that is a substring + # of the imported module, and the sync flavour of the public surface lives + # at ably.pubsub.server.sync rather than under ably.pubsub.sync. + _IMPORTS_REPLACE["ably.pubsub.server"] = "ably.pubsub.server.sync" + _IMPORTS_REPLACE["ably.pubsub"] = "ably.pubsub.sync" # here... for class_name in rename_classes: _CLASS_RENAME[class_name] = f"{class_name}Sync" _STRING_REPLACE["Auth"] = "AuthSync" - - src_dir_path = os.path.join(os.getcwd(), "ably") - dest_dir_path = os.path.join(os.getcwd(), "ably", "sync") + # The sync flavour's factory lives in ably.pubsub.server.sync. There is no + # sync realtime factory, so create_realtime_client is left pointing at the + # asynchronous one, which is the right advice for that case. + _STRING_REPLACE['ably.pubsub.server.create_http_client'] = \ + 'ably.pubsub.server.sync.create_http_client' + + src_dir_path = os.path.join(os.getcwd(), "ably", "pubsub") + dest_dir_path = os.path.join(os.getcwd(), "ably", "pubsub", "sync") + # ably/pubsub/server is the hand-written public re-export surface; its + # synchronous flavour lives in ably/pubsub/server/sync.py, not under sync/. + server_dir_path = os.path.join(os.getcwd(), "ably", "pubsub", "server") relevant_src_files = (set(find_files(src_dir_path, "*.py")) - - set(find_files(dest_dir_path, "*.py"))) + set(find_files(dest_dir_path, "*.py")) - + set(find_files(server_dir_path, "*.py"))) unasync_files(list(relevant_src_files), [Rule(fromdir=src_dir_path, todir=dest_dir_path)]) @@ -269,14 +282,16 @@ def run(): _IMPORTS_REPLACE["test.ably"] = "test.ably.sync" _STRING_REPLACE['/../assets/testAppSpec.json'] = '/../../assets/testAppSpec.json' - _STRING_REPLACE['ably.rest.auth.Auth.request_token'] = 'ably.sync.rest.auth.AuthSync.request_token' - _STRING_REPLACE['ably.rest.auth.TokenRequest'] = 'ably.sync.rest.auth.TokenRequest' - _STRING_REPLACE['ably.rest.rest.Http.post'] = 'ably.sync.rest.rest.HttpSync.post' + _STRING_REPLACE['ably.pubsub.http.auth.Auth.request_token'] = \ + 'ably.pubsub.sync.http.auth.AuthSync.request_token' + _STRING_REPLACE['ably.pubsub.http.auth.TokenRequest'] = 'ably.pubsub.sync.http.auth.TokenRequest' + _STRING_REPLACE['ably.pubsub.http.http.Http.post'] = 'ably.pubsub.sync.http.http.HttpSync.post' _STRING_REPLACE['httpx.AsyncClient.send'] = 'httpx.Client.send' - _STRING_REPLACE['ably.util.exceptions.AblyException.raise_for_response'] = \ - 'ably.sync.util.exceptions.AblyException.raise_for_response' - _STRING_REPLACE['ably.rest.rest.AblyRest.time'] = 'ably.sync.rest.rest.AblyRestSync.time' - _STRING_REPLACE['ably.rest.auth.Auth._timestamp'] = 'ably.sync.rest.auth.AuthSync._timestamp' + _STRING_REPLACE['ably.pubsub.util.exceptions.AblyException.raise_for_response'] = \ + 'ably.pubsub.sync.util.exceptions.AblyException.raise_for_response' + _STRING_REPLACE['ably.pubsub.http.http.DefaultPubSubHttpClient.time'] = \ + 'ably.pubsub.sync.http.http.DefaultPubSubHttpClientSync.time' + _STRING_REPLACE['ably.pubsub.http.auth.Auth._timestamp'] = 'ably.pubsub.sync.http.auth.AuthSync._timestamp' # round 1 src_dir_path = os.path.join(os.getcwd(), "test", "ably") @@ -287,8 +302,8 @@ def run(): unasync_files(src_files, [Rule(fromdir=src_dir_path, todir=dest_dir_path)]) # round 2 - src_dir_path = os.path.join(os.getcwd(), "test", "ably", "rest") - dest_dir_path = os.path.join(os.getcwd(), "test", "ably", "sync", "rest") + src_dir_path = os.path.join(os.getcwd(), "test", "ably", "http") + dest_dir_path = os.path.join(os.getcwd(), "test", "ably", "sync", "http") src_files = find_files(src_dir_path, "*.py") unasync_files(src_files, [Rule(fromdir=src_dir_path, todir=dest_dir_path, output_file_prefix="sync_")]) diff --git a/ably/pubsub/server/__init__.py b/ably/pubsub/server/__init__.py new file mode 100644 index 00000000..41efc61e --- /dev/null +++ b/ably/pubsub/server/__init__.py @@ -0,0 +1,279 @@ +"""The Ably Pub/Sub client for servers. + +Servers are trusted environments which typically authenticate with an API key, +and whose connections are exempt from monthly-active-user counting. This +package names that side, so the client an application reaches for is the one +whose package matches where it runs. + +Build a client with :func:`create_http_client` or +:func:`create_realtime_client`; the classes behind them are internal and +reject direct construction. Annotate against the prototypes they return, +:class:`~ably.pubsub.prototypes.PubSubHttpClient` and +:class:`~ably.pubsub.prototypes.PubSubRealtimeClient`. + +This is the public API of the ``ably-pubsub-server`` distribution. Both +``ably`` and ``ably.pubsub`` are PEP 420 namespace packages shared with the +other ``ably-*`` distributions, so neither carries an ``__init__.py`` of its +own and neither exports anything; everything the library offers is reachable +from here. +""" + +from ably.pubsub.http.auth import Auth +from ably.pubsub.http.channel import Channels as HttpChannels +from ably.pubsub.http.http import DefaultPubSubHttpClient as _DefaultPubSubHttpClient +from ably.pubsub.http.push import Push +from ably.pubsub.prototypes import PubSubHttpClient, PubSubRealtimeClient +from ably.pubsub.realtime.channel import Channels as RealtimeChannels +from ably.pubsub.realtime.connection import Connection +from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient as _DefaultPubSubRealtimeClient +from ably.pubsub.request.paginatedresult import HttpPaginatedResponse, PaginatedResult +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.capability import Capability +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelsubscription import PushChannelSubscription +from ably.pubsub.types.device import DeviceDetails +from ably.pubsub.types.message import Message, MessageAction, MessageVersion +from ably.pubsub.types.operations import MessageOperation, PublishResult, UpdateDeleteResult +from ably.pubsub.types.options import Options, VCDiffDecoder +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.construction import factory_construction +from ably.pubsub.util.crypto import CipherParams +from ably.pubsub.util.exceptions import AblyAuthException, AblyException, IncompatibleClientIdException +from ably.pubsub.vcdiff.defaultvcdiffdecoder import AblyVCDiffDecoder +from ably.pubsub.version import api_version, lib_version + + +def create_http_client(**kwargs) -> PubSubHttpClient: + """Create a server Pub/Sub client that operates entirely over HTTP. + + Handles publish, history, presence reads, stats and token issuing. Give the client one + of `key`, `token` or `token_details` to authenticate, or configure token auth through + `auth_callback` or `auth_url`. + + Parameters + ---------- + **kwargs: client options + **Credentials** + + key: str + A valid Ably API key, in `name:secret` form. Mutually exclusive with `key_name` + and `key_secret`. + token: str + A valid token string to authenticate with in place of an API key. + token_details: TokenDetails + A TokenDetails instance to authenticate with in place of an API key. + key_name: str + The first half of an API key, as an alternative to passing `key`. + key_secret: str + The second half of an API key, as an alternative to passing `key`. + + **Token authentication** + + auth_callback: callable + Called when a new token is needed, and returns a token string, a TokenDetails, + a TokenRequest or a signed token request dict. Prefer this to `auth_url` when + the token source is in-process. + auth_url: str + A URL to request a token from when a new token is needed. The client issues a + request to this URL and uses the response as the token. + auth_method: str + The HTTP method used for the `auth_url` request. Defaults to `GET`. + auth_headers: dict + Headers to include in the `auth_url` request. + auth_params: dict + Query parameters to include in the `auth_url` request. + use_token_auth: bool + Force token authentication even when an API key is available. By default the + client uses basic auth where a key is present. + query_time: bool + Query the Ably service for the current time when signing token requests, rather + than trusting the local clock. Defaults to False. + default_token_params: dict + Parameters applied to every token request this client makes. + + **Connection** + + client_id: str + The ID this client identifies as. Messages it publishes carry this ID, and it + cannot be changed once the client has authenticated. + endpoint: str + Either a routing policy name or a fully qualified domain name to connect to. + Defaults to `main`. Incompatible with `environment`, `rest_host` and + `realtime_host`. + tls: bool + Whether the client should use TLS. Defaults to True. + port: int + The non-TLS port to connect to. Defaults to 80. + tls_port: int + The TLS port to connect to. Defaults to 443. + fallback_hosts: list[str] + Hosts to fall back to when the primary endpoint is unreachable. Set this only + if Ably has provided you with a custom set. + fallback_retry_timeout: float + How long (in milliseconds) a fallback host stays preferred once the client has + failed over to it. The default is 10 minutes (600000 milliseconds). + + **HTTP behaviour** + + http_open_timeout: float + Timeout (in seconds) for establishing an HTTP connection. The default is 4 + seconds. + http_request_timeout: float + Timeout (in seconds) for a complete HTTP request. The default is 10 seconds. + http_max_retry_duration: float + How long (in seconds) the client keeps retrying a request across hosts before + giving up. The default is 15 seconds. + http_max_retry_count: int + The most hosts the client will try for a single request. + + **Protocol** + + use_binary_protocol: bool + Encode messages with MessagePack rather than JSON. Defaults to True. + idempotent_rest_publishing: bool + Assign an ID to published messages so that a retried publish is not duplicated. + Defaults to True. + add_request_ids: bool + Add a `request_id` query parameter to every request, which makes a request + traceable in Ably's logs. Defaults to False. + log_level: int + Verbosity of the library's own logging. Defaults to 0. + + **Deprecated** + + rest_host: str + Deprecated: this option will be removed in a future version. Use `endpoint`. + environment: str + Deprecated: this option will be removed in a future version. Use `endpoint`. + + Returns + ------- + PubSubHttpClient + A client ready to use. It opens no connection until its first request. + + Raises + ------ + ValueError + If `key` is combined with `key_name` or `key_secret`. + AblyException + If no means of authenticating is given, or if `endpoint` is combined with + `environment`, `rest_host` or `realtime_host`. + """ + with factory_construction(): + return _DefaultPubSubHttpClient(**kwargs) + + +def create_realtime_client(**kwargs) -> PubSubRealtimeClient: + """Create a server Pub/Sub client with a persistent realtime connection. + + Everything the HTTP client does, plus subscribing to channels and entering presence. + Accepts every option :func:`create_http_client` does — see there for credentials, + token authentication, connection and HTTP behaviour — and the following in addition. + + Parameters + ---------- + **kwargs: client options + **Connection lifecycle** + + loop: asyncio.AbstractEventLoop + The event loop the client runs on. Defaults to the running loop; the client + logs a warning if it is created outside one. + auto_connect: bool + Connect as soon as the client is created. Set this to False to connect later + with `connect()`. Defaults to True. + recover: str + A recovery key from a previous connection, used to resume that connection's + state and message continuity rather than starting fresh. + queue_messages: bool + Hold messages published while the connection is not yet established and send + them once it is, rather than failing them. Defaults to True. + transport_params: dict + Additional query parameters to send when opening the realtime connection. + + **Timeouts and retries** + + realtime_request_timeout: float + How long (in milliseconds) to wait for acknowledgement of an operation + performed over the realtime connection, such as establishing a connection or + sending a HEARTBEAT, CONNECT, ATTACH, DETACH or CLOSE request. The default is + 10 seconds (10000 milliseconds). + disconnected_retry_timeout: float + How long (in milliseconds) to stay DISCONNECTED before attempting to reconnect + automatically. The default is 15 seconds (15000 milliseconds). + suspended_retry_timeout: float + How long (in milliseconds) to stay SUSPENDED before attempting to reconnect + automatically. The default is 30 seconds (30000 milliseconds). + channel_retry_timeout: float + How long (in milliseconds) a channel stays SUSPENDED, following a server + initiated DETACHED, before the client re-attaches it — provided the connection + is CONNECTED. The default is 15 seconds (15000 milliseconds). + connectivity_check_url: str + The URL the client requests to decide whether the internet is reachable. On + failing to reach the primary endpoint, a success response from this URL tells + the client to try a fallback host instead of giving up. + + **Deltas** + + vcdiff_decoder: VCDiffDecoder + The decoder used to apply delta-encoded messages on channels configured with + `params={"delta": "vcdiff"}`. Pass an :class:`AblyVCDiffDecoder`. + + **Deprecated** + + realtime_host: str + Deprecated: this option will be removed in a future version. Use `endpoint`. + + Returns + ------- + PubSubRealtimeClient + A client which, unless `auto_connect=False`, is already connecting. + + Raises + ------ + ValueError + If `key` is combined with `key_name` or `key_secret`. + AblyException + If no means of authenticating is given, or if `endpoint` is combined with + `environment`, `rest_host` or `realtime_host`. + """ + with factory_construction(): + return _DefaultPubSubRealtimeClient(**kwargs) + + +__all__ = [ + 'AblyAuthException', + 'AblyException', + 'AblyVCDiffDecoder', + 'Annotation', + 'AnnotationAction', + 'Auth', + 'Capability', + 'ChannelMode', + 'ChannelOptions', + 'CipherParams', + 'Connection', + 'DeviceDetails', + 'HttpChannels', + 'HttpPaginatedResponse', + 'IncompatibleClientIdException', + 'Message', + 'MessageAction', + 'MessageOperation', + 'MessageVersion', + 'Options', + 'PaginatedResult', + 'PubSubHttpClient', + 'PubSubRealtimeClient', + 'PublishResult', + 'Push', + 'PushChannelSubscription', + 'RealtimeChannels', + 'TokenDetails', + 'UpdateDeleteResult', + 'VCDiffDecoder', + 'api_version', + 'create_http_client', + 'create_realtime_client', + 'lib_version', +] diff --git a/ably/pubsub/server/sync.py b/ably/pubsub/server/sync.py new file mode 100644 index 00000000..5ccab431 --- /dev/null +++ b/ably/pubsub/server/sync.py @@ -0,0 +1,88 @@ +"""The synchronous flavour of the Ably Pub/Sub client for servers. + +Exposes the same HTTP surface as :mod:`ably.pubsub.server`, generated without +``async``/``await`` so it can be driven from a thread rather than an event +loop. There is no synchronous realtime client, so a server that needs to +subscribe to channels should use :func:`ably.pubsub.server.create_realtime_client` +instead. +""" + +from ably.pubsub.sync.http.auth import AuthSync +from ably.pubsub.sync.http.channel import ChannelsSync as HttpChannelsSync +from ably.pubsub.sync.http.http import DefaultPubSubHttpClientSync as _DefaultPubSubHttpClientSync +from ably.pubsub.sync.http.push import PushSync +from ably.pubsub.sync.prototypes import PubSubHttpClient +from ably.pubsub.sync.request.paginatedresult import HttpPaginatedResponseSync, PaginatedResultSync +from ably.pubsub.sync.types.annotation import Annotation, AnnotationAction +from ably.pubsub.sync.types.capability import Capability +from ably.pubsub.sync.types.channelmode import ChannelMode +from ably.pubsub.sync.types.channeloptions import ChannelOptions +from ably.pubsub.sync.types.channelsubscription import PushChannelSubscription +from ably.pubsub.sync.types.device import DeviceDetails +from ably.pubsub.sync.types.message import Message, MessageAction, MessageVersion +from ably.pubsub.sync.types.operations import MessageOperation, PublishResult, UpdateDeleteResult +from ably.pubsub.sync.types.options import Options, VCDiffDecoder +from ably.pubsub.sync.types.tokendetails import TokenDetails +from ably.pubsub.sync.util.construction import factory_construction +from ably.pubsub.sync.util.crypto import CipherParams +from ably.pubsub.sync.util.exceptions import AblyAuthException, AblyException, IncompatibleClientIdException +from ably.pubsub.sync.vcdiff.defaultvcdiffdecoder import AblyVCDiffDecoder +from ably.pubsub.version import api_version, lib_version + + +def create_http_client(**kwargs) -> PubSubHttpClient: + """Create a synchronous server Pub/Sub client that operates entirely over HTTP. + + Accepts every option :func:`ably.pubsub.server.create_http_client` does — see there + for credentials, token authentication, connection and HTTP behaviour — and behaves + identically, without the event loop. The realtime-only options have no effect here. + + Returns + ------- + PubSubHttpClient + A client ready to use. It opens no connection until its first request. + + Raises + ------ + ValueError + If `key` is combined with `key_name` or `key_secret`. + AblyException + If no means of authenticating is given, or if `endpoint` is combined with + `environment`, `rest_host` or `realtime_host`. + """ + with factory_construction(): + return _DefaultPubSubHttpClientSync(**kwargs) + + +__all__ = [ + 'AblyAuthException', + 'AblyException', + 'AblyVCDiffDecoder', + 'Annotation', + 'AnnotationAction', + 'AuthSync', + 'Capability', + 'ChannelMode', + 'ChannelOptions', + 'CipherParams', + 'DeviceDetails', + 'HttpChannelsSync', + 'HttpPaginatedResponseSync', + 'IncompatibleClientIdException', + 'Message', + 'MessageAction', + 'MessageOperation', + 'MessageVersion', + 'Options', + 'PaginatedResultSync', + 'PubSubHttpClient', + 'PublishResult', + 'PushChannelSubscription', + 'PushSync', + 'TokenDetails', + 'UpdateDeleteResult', + 'VCDiffDecoder', + 'api_version', + 'create_http_client', + 'lib_version', +] diff --git a/ably/transport/__init__.py b/ably/pubsub/transport/__init__.py similarity index 100% rename from ably/transport/__init__.py rename to ably/pubsub/transport/__init__.py diff --git a/ably/transport/defaults.py b/ably/pubsub/transport/defaults.py similarity index 100% rename from ably/transport/defaults.py rename to ably/pubsub/transport/defaults.py diff --git a/ably/transport/websockettransport.py b/ably/pubsub/transport/websockettransport.py similarity index 96% rename from ably/transport/websockettransport.py rename to ably/pubsub/transport/websockettransport.py index dbde5413..7177732c 100644 --- a/ably/transport/websockettransport.py +++ b/ably/pubsub/transport/websockettransport.py @@ -10,13 +10,13 @@ import msgpack -from ably.http.httputils import HttpUtils -from ably.transport.defaults import Defaults -from ably.types.connectiondetails import ConnectionDetails -from ably.types.operations import PublishResult -from ably.util.clock import select_clock -from ably.util.eventemitter import EventEmitter -from ably.util.exceptions import AblyException +from ably.pubsub.request.httputils import HttpUtils +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.types.connectiondetails import ConnectionDetails +from ably.pubsub.types.operations import PublishResult +from ably.pubsub.util.clock import select_clock +from ably.pubsub.util.eventemitter import EventEmitter +from ably.pubsub.util.exceptions import AblyException try: # websockets 15+ preferred imports @@ -30,7 +30,7 @@ from websockets.exceptions import ConnectionClosedOK, WebSocketException if TYPE_CHECKING: - from ably.realtime.connection import ConnectionManager + from ably.pubsub.realtime.connection import ConnectionManager log = logging.getLogger(__name__) diff --git a/ably/types/__init__.py b/ably/pubsub/types/__init__.py similarity index 100% rename from ably/types/__init__.py rename to ably/pubsub/types/__init__.py diff --git a/ably/types/annotation.py b/ably/pubsub/types/annotation.py similarity index 98% rename from ably/types/annotation.py rename to ably/pubsub/types/annotation.py index 66ce5271..98e845a3 100644 --- a/ably/types/annotation.py +++ b/ably/pubsub/types/annotation.py @@ -1,11 +1,11 @@ import logging from enum import IntEnum -from ably.types.mixins import EncodeDataMixin -from ably.types.typedbuffer import TypedBuffer -from ably.util.crypto import CipherData -from ably.util.encoding import encode_data -from ably.util.helper import to_text +from ably.pubsub.types.mixins import EncodeDataMixin +from ably.pubsub.types.typedbuffer import TypedBuffer +from ably.pubsub.util.crypto import CipherData +from ably.pubsub.util.encoding import encode_data +from ably.pubsub.util.helper import to_text log = logging.getLogger(__name__) diff --git a/ably/types/authoptions.py b/ably/pubsub/types/authoptions.py similarity index 98% rename from ably/types/authoptions.py rename to ably/pubsub/types/authoptions.py index 7ee06af7..18f0fb30 100644 --- a/ably/types/authoptions.py +++ b/ably/pubsub/types/authoptions.py @@ -1,4 +1,4 @@ -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException class AuthOptions: diff --git a/ably/types/capability.py b/ably/pubsub/types/capability.py similarity index 100% rename from ably/types/capability.py rename to ably/pubsub/types/capability.py diff --git a/ably/types/channeldetails.py b/ably/pubsub/types/channeldetails.py similarity index 100% rename from ably/types/channeldetails.py rename to ably/pubsub/types/channeldetails.py diff --git a/ably/types/channelmode.py b/ably/pubsub/types/channelmode.py similarity index 96% rename from ably/types/channelmode.py rename to ably/pubsub/types/channelmode.py index 23ed735c..b16b355d 100644 --- a/ably/types/channelmode.py +++ b/ably/pubsub/types/channelmode.py @@ -2,7 +2,7 @@ from enum import Enum -from ably.types.flags import Flag +from ably.pubsub.types.flags import Flag class ChannelMode(int, Enum): diff --git a/ably/types/channeloptions.py b/ably/pubsub/types/channeloptions.py similarity index 94% rename from ably/types/channeloptions.py rename to ably/pubsub/types/channeloptions.py index 3e5052c6..9f7a99e1 100644 --- a/ably/types/channeloptions.py +++ b/ably/pubsub/types/channeloptions.py @@ -2,9 +2,9 @@ from typing import Any -from ably.types.channelmode import ChannelMode -from ably.util.crypto import CipherParams -from ably.util.exceptions import AblyException +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.util.crypto import CipherParams +from ably.pubsub.util.exceptions import AblyException class ChannelOptions: diff --git a/ably/types/channelstate.py b/ably/pubsub/types/channelstate.py similarity index 89% rename from ably/types/channelstate.py rename to ably/pubsub/types/channelstate.py index dcb68d67..fee06ce2 100644 --- a/ably/types/channelstate.py +++ b/ably/pubsub/types/channelstate.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException class ChannelState(str, Enum): diff --git a/ably/types/channelsubscription.py b/ably/pubsub/types/channelsubscription.py similarity index 98% rename from ably/types/channelsubscription.py rename to ably/pubsub/types/channelsubscription.py index b4c0dbf8..9cc386c9 100644 --- a/ably/types/channelsubscription.py +++ b/ably/pubsub/types/channelsubscription.py @@ -1,4 +1,4 @@ -from ably.util import case +from ably.pubsub.util import case class PushChannelSubscription: diff --git a/ably/types/connectiondetails.py b/ably/pubsub/types/connectiondetails.py similarity index 100% rename from ably/types/connectiondetails.py rename to ably/pubsub/types/connectiondetails.py diff --git a/ably/types/connectionerrors.py b/ably/pubsub/types/connectionerrors.py similarity index 84% rename from ably/types/connectionerrors.py rename to ably/pubsub/types/connectionerrors.py index bb2fa1f4..4b2157d8 100644 --- a/ably/types/connectionerrors.py +++ b/ably/pubsub/types/connectionerrors.py @@ -1,5 +1,5 @@ -from ably.types.connectionstate import ConnectionState -from ably.util.exceptions import AblyException +from ably.pubsub.types.connectionstate import ConnectionState +from ably.pubsub.util.exceptions import AblyException ConnectionErrors = { ConnectionState.DISCONNECTED: AblyException( diff --git a/ably/types/connectionstate.py b/ably/pubsub/types/connectionstate.py similarity index 93% rename from ably/types/connectionstate.py rename to ably/pubsub/types/connectionstate.py index ec958358..fde91caa 100644 --- a/ably/types/connectionstate.py +++ b/ably/pubsub/types/connectionstate.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException class ConnectionState(str, Enum): diff --git a/ably/types/device.py b/ably/pubsub/types/device.py similarity index 98% rename from ably/types/device.py rename to ably/pubsub/types/device.py index c2b84ee5..3948b751 100644 --- a/ably/types/device.py +++ b/ably/pubsub/types/device.py @@ -1,4 +1,4 @@ -from ably.util import case +from ably.pubsub.util import case DevicePushTransportType = {'fcm', 'gcm', 'apns', 'web'} DevicePlatform = {'android', 'ios', 'browser'} diff --git a/ably/types/flags.py b/ably/pubsub/types/flags.py similarity index 100% rename from ably/types/flags.py rename to ably/pubsub/types/flags.py diff --git a/ably/types/message.py b/ably/pubsub/types/message.py similarity index 97% rename from ably/types/message.py rename to ably/pubsub/types/message.py index 2d84350b..93a20f0c 100644 --- a/ably/types/message.py +++ b/ably/pubsub/types/message.py @@ -1,12 +1,12 @@ import logging from enum import IntEnum -from ably.types.mixins import DeltaExtras, EncodeDataMixin -from ably.types.typedbuffer import TypedBuffer -from ably.util.crypto import CipherData -from ably.util.encoding import encode_data -from ably.util.exceptions import AblyException -from ably.util.helper import to_text +from ably.pubsub.types.mixins import DeltaExtras, EncodeDataMixin +from ably.pubsub.types.typedbuffer import TypedBuffer +from ably.pubsub.util.crypto import CipherData +from ably.pubsub.util.encoding import encode_data +from ably.pubsub.util.exceptions import AblyException +from ably.pubsub.util.helper import to_text log = logging.getLogger(__name__) diff --git a/ably/types/mixins.py b/ably/pubsub/types/mixins.py similarity index 98% rename from ably/types/mixins.py rename to ably/pubsub/types/mixins.py index c5fad258..976da3b1 100644 --- a/ably/types/mixins.py +++ b/ably/pubsub/types/mixins.py @@ -3,8 +3,8 @@ import json import logging -from ably.util.crypto import CipherData -from ably.util.exceptions import AblyException +from ably.pubsub.util.crypto import CipherData +from ably.pubsub.util.exceptions import AblyException log = logging.getLogger(__name__) diff --git a/ably/types/operations.py b/ably/pubsub/types/operations.py similarity index 100% rename from ably/types/operations.py rename to ably/pubsub/types/operations.py diff --git a/ably/types/options.py b/ably/pubsub/types/options.py similarity index 98% rename from ably/types/options.py rename to ably/pubsub/types/options.py index 5030a7cd..51f6016e 100644 --- a/ably/types/options.py +++ b/ably/pubsub/types/options.py @@ -2,9 +2,9 @@ import random from abc import ABC, abstractmethod -from ably.transport.defaults import Defaults -from ably.types.authoptions import AuthOptions -from ably.util.exceptions import AblyException +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.types.authoptions import AuthOptions +from ably.pubsub.util.exceptions import AblyException log = logging.getLogger(__name__) @@ -81,7 +81,7 @@ def __init__(self, client_id=None, log_level=0, tls=True, rest_host=None, realti ) if idempotent_rest_publishing is None: - from ably import api_version + from ably.pubsub.version import api_version idempotent_rest_publishing = api_version >= '1.2' if environment is not None and endpoint is None: diff --git a/ably/types/presence.py b/ably/pubsub/types/presence.py similarity index 96% rename from ably/types/presence.py rename to ably/pubsub/types/presence.py index 669f1ede..66cbdea7 100644 --- a/ably/types/presence.py +++ b/ably/pubsub/types/presence.py @@ -1,11 +1,11 @@ from datetime import datetime, timedelta from urllib import parse -from ably.http.paginatedresult import PaginatedResult -from ably.types.mixins import EncodeDataMixin -from ably.types.typedbuffer import TypedBuffer -from ably.util.crypto import CipherData -from ably.util.encoding import encode_data +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.mixins import EncodeDataMixin +from ably.pubsub.types.typedbuffer import TypedBuffer +from ably.pubsub.util.crypto import CipherData +from ably.pubsub.util.encoding import encode_data def _ms_since_epoch(dt): diff --git a/ably/types/stats.py b/ably/pubsub/types/stats.py similarity index 100% rename from ably/types/stats.py rename to ably/pubsub/types/stats.py diff --git a/ably/types/testoptions.py b/ably/pubsub/types/testoptions.py similarity index 93% rename from ably/types/testoptions.py rename to ably/pubsub/types/testoptions.py index bc50f702..4c795411 100644 --- a/ably/types/testoptions.py +++ b/ably/pubsub/types/testoptions.py @@ -13,7 +13,7 @@ class TestOptions: context manager yielding an object supporting `__aiter__`, `send` and `close`. - `clock`: the source of time the client reads, in place of - `ably.util.clock.Clock`. It supplies `now_ms()`, `monotonic_ms()` and + `ably.pubsub.util.clock.Clock`. It supplies `now_ms()`, `monotonic_ms()` and `timer(timeout_ms, callback)`, where `callback` is either a coroutine function or a plain callable and the return value has a `cancel()` method. diff --git a/ably/types/tokendetails.py b/ably/pubsub/types/tokendetails.py similarity index 97% rename from ably/types/tokendetails.py rename to ably/pubsub/types/tokendetails.py index 1980308d..29c7dd24 100644 --- a/ably/types/tokendetails.py +++ b/ably/pubsub/types/tokendetails.py @@ -1,7 +1,7 @@ import json import time -from ably.types.capability import Capability +from ably.pubsub.types.capability import Capability class TokenDetails: diff --git a/ably/types/tokenrequest.py b/ably/pubsub/types/tokenrequest.py similarity index 100% rename from ably/types/tokenrequest.py rename to ably/pubsub/types/tokenrequest.py diff --git a/ably/types/typedbuffer.py b/ably/pubsub/types/typedbuffer.py similarity index 100% rename from ably/types/typedbuffer.py rename to ably/pubsub/types/typedbuffer.py diff --git a/ably/pubsub/util/__init__.py b/ably/pubsub/util/__init__.py new file mode 100644 index 00000000..ac235ae9 --- /dev/null +++ b/ably/pubsub/util/__init__.py @@ -0,0 +1,10 @@ +import logging + +# Silence "no handler" output for every logger in the library. This would +# normally go in the top-level package's __init__.py, but `ably.pubsub` is a +# PEP 420 namespace package and so has none; `ably.pubsub.util` is the one +# module every entry point, async and sync alike, imports on its way in. The +# unasync'd copy of this module runs the same line, hence the guard. +_root_logger = logging.getLogger('ably.pubsub') +if not any(isinstance(handler, logging.NullHandler) for handler in _root_logger.handlers): + _root_logger.addHandler(logging.NullHandler()) diff --git a/ably/util/case.py b/ably/pubsub/util/case.py similarity index 100% rename from ably/util/case.py rename to ably/pubsub/util/case.py diff --git a/ably/util/clock.py b/ably/pubsub/util/clock.py similarity index 97% rename from ably/util/clock.py rename to ably/pubsub/util/clock.py index 00aef3fd..0d4dd0ed 100644 --- a/ably/util/clock.py +++ b/ably/pubsub/util/clock.py @@ -1,7 +1,7 @@ import time from typing import Callable -from ably.util.helper import Timer +from ably.pubsub.util.helper import Timer class Clock: diff --git a/ably/pubsub/util/construction.py b/ably/pubsub/util/construction.py new file mode 100644 index 00000000..f1a0cd94 --- /dev/null +++ b/ably/pubsub/util/construction.py @@ -0,0 +1,41 @@ +"""Enforcement of the factories as the only way to build a client. + +:mod:`ably.pubsub.server` exposes ``create_http_client`` and +``create_realtime_client`` rather than the client classes themselves, so that +the package a client comes from names the side the application runs on. The +classes stay importable for the library's own use and reject construction from +anywhere else. +""" + +from contextlib import contextmanager +from contextvars import ContextVar + +# True for the duration of a factory call. A ContextVar rather than a module +# global so that concurrent tasks cannot observe each other's state. +_in_factory: ContextVar = ContextVar('ably_pubsub_in_factory', default=False) + + +@contextmanager +def factory_construction(): + """Permit client construction within this block. + + For use by Ably's own factory functions only. + """ + token = _in_factory.set(True) + try: + yield + finally: + _in_factory.reset(token) + + +def reject_direct_construction(cls: type, factory: str) -> None: + """Raise unless we are inside a factory call. + + `factory` names the entry point the caller should have reached for, so + that the error says what to do rather than only what went wrong. + """ + if _in_factory.get(): + return + raise TypeError( + f'{cls.__name__} cannot be constructed directly. Use {factory} instead.' + ) diff --git a/ably/util/crypto.py b/ably/pubsub/util/crypto.py similarity index 98% rename from ably/util/crypto.py rename to ably/pubsub/util/crypto.py index 8d8ddfd9..88a62f92 100644 --- a/ably/util/crypto.py +++ b/ably/pubsub/util/crypto.py @@ -7,8 +7,8 @@ except ImportError: from .nocrypto import AES, Random -from ably.types.typedbuffer import TypedBuffer -from ably.util.exceptions import AblyException +from ably.pubsub.types.typedbuffer import TypedBuffer +from ably.pubsub.util.exceptions import AblyException log = logging.getLogger(__name__) diff --git a/ably/util/encoding.py b/ably/pubsub/util/encoding.py similarity index 91% rename from ably/util/encoding.py rename to ably/pubsub/util/encoding.py index 5187aec2..7f4b89da 100644 --- a/ably/util/encoding.py +++ b/ably/pubsub/util/encoding.py @@ -2,8 +2,8 @@ import json from typing import Any -from ably.util.crypto import CipherData -from ably.util.exceptions import AblyException +from ably.pubsub.util.crypto import CipherData +from ably.pubsub.util.exceptions import AblyException def encode_data(data: Any, encoding_array: list, binary: bool = False): diff --git a/ably/util/eventemitter.py b/ably/pubsub/util/eventemitter.py similarity index 99% rename from ably/util/eventemitter.py rename to ably/pubsub/util/eventemitter.py index 74f0beb6..491b0a00 100644 --- a/ably/util/eventemitter.py +++ b/ably/pubsub/util/eventemitter.py @@ -3,7 +3,7 @@ from pyee.asyncio import AsyncIOEventEmitter -from ably.util.helper import is_callable_or_coroutine +from ably.pubsub.util.helper import is_callable_or_coroutine # pyee's event emitter doesn't support attaching a listener to all events # so to patch it, we create a wrapper which uses two event emitters, one diff --git a/ably/util/exceptions.py b/ably/pubsub/util/exceptions.py similarity index 100% rename from ably/util/exceptions.py rename to ably/pubsub/util/exceptions.py diff --git a/ably/util/helper.py b/ably/pubsub/util/helper.py similarity index 98% rename from ably/util/helper.py rename to ably/pubsub/util/helper.py index 4d37d4f4..934a1af1 100644 --- a/ably/util/helper.py +++ b/ably/pubsub/util/helper.py @@ -9,7 +9,7 @@ import msgpack -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException def get_random_id(): diff --git a/ably/util/nocrypto.py b/ably/pubsub/util/nocrypto.py similarity index 100% rename from ably/util/nocrypto.py rename to ably/pubsub/util/nocrypto.py diff --git a/ably/util/__init__.py b/ably/pubsub/vcdiff/__init__.py similarity index 100% rename from ably/util/__init__.py rename to ably/pubsub/vcdiff/__init__.py diff --git a/ably/vcdiff/defaultvcdiffdecoder.py b/ably/pubsub/vcdiff/defaultvcdiffdecoder.py similarity index 88% rename from ably/vcdiff/defaultvcdiffdecoder.py rename to ably/pubsub/vcdiff/defaultvcdiffdecoder.py index ae2d3263..07af3b9e 100644 --- a/ably/vcdiff/defaultvcdiffdecoder.py +++ b/ably/pubsub/vcdiff/defaultvcdiffdecoder.py @@ -5,13 +5,13 @@ It implements the VCDiffDecoder interface. Usage: - from ably.vcdiff import AblyVCDiffDecoder, AblyRealtime + from ably.pubsub.server import AblyVCDiffDecoder, ChannelOptions, create_realtime_client # Create VCDiff decoder vcdiff_decoder = AblyVCDiffDecoder() # Create client with decoder - client = AblyRealtime(key="your-key", vcdiff_decoder=vcdiff_decoder) + client = create_realtime_client(key="your-key", vcdiff_decoder=vcdiff_decoder) # Get channel with delta enabled channel = client.channels.get("test", ChannelOptions(params={"delta": "vcdiff"})) @@ -19,8 +19,8 @@ import logging -from ably.types.options import VCDiffDecoder -from ably.util.exceptions import AblyException +from ably.pubsub.types.options import VCDiffDecoder +from ably.pubsub.util.exceptions import AblyException log = logging.getLogger(__name__) diff --git a/ably/pubsub/version.py b/ably/pubsub/version.py new file mode 100644 index 00000000..8389a068 --- /dev/null +++ b/ably/pubsub/version.py @@ -0,0 +1,9 @@ +"""Version constants for the Ably Pub/Sub library. + +These live in a module of their own rather than in a package ``__init__.py`` +because ``ably.pubsub`` is a PEP 420 namespace package, shared with the other +``ably-pubsub-*`` distributions, and so cannot carry one. +""" + +api_version = '5' +lib_version = '3.1.4' diff --git a/ably/vcdiff/__init__.py b/ably/vcdiff/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/pyproject.toml b/pyproject.toml index 342f92c2..2e0d5eed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] -name = "ably" +name = "ably-pubsub-server" version = "3.1.4" -description = "Python REST and Realtime client library SDK for Ably realtime messaging service" +description = "Python REST and Realtime server client library SDK for Ably Pub/Sub" readme = "LONG_DESCRIPTION.rst" -requires-python = ">=3.7" +requires-python = ">=3.8" license = { text = "Apache-2.0" } authors = [ { name = "Ably", email = "support@ably.com" } @@ -15,7 +15,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -59,7 +58,7 @@ dev = [ ] [project.scripts] -unasync = "ably.scripts.unasync:run" +unasync = "ably.pubsub.scripts.unasync:run" [project.urls] Homepage = "https://ably.com" @@ -88,7 +87,9 @@ exclude = [ [tool.hatch.build.targets.wheel] ignore-vcs = true -packages = ["ably"] +# `ably` is a PEP 420 namespace package shared with the other ably-* distributions, +# so it must not be shipped with an __init__.py of its own. +only-include = ["ably/pubsub"] [tool.pytest.ini_options] timeout = 30 @@ -102,7 +103,7 @@ explicit = true [tool.ruff] line-length = 115 extend-exclude = [ - "ably/sync", + "ably/pubsub/sync", "test/ably/sync", ] @@ -116,3 +117,10 @@ ignore = [ [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] # imported but unused + +[tool.ruff.lint.isort] +# `ably.pubsub.sync` and `test.ably.sync` only exist once unasync has generated +# them, and the lint job does not generate them. Left to resolve imports itself, +# ruff files those modules as third-party there and first-party here, so the same +# commit lints clean locally and fails in CI. State it instead of inferring it. +known-first-party = ["ably", "test"] diff --git a/test/ably/rest/authheaders_test.py b/test/ably/http/authheaders_test.py similarity index 95% rename from test/ably/rest/authheaders_test.py rename to test/ably/http/authheaders_test.py index 69f0ad1b..ad4ffaa9 100644 --- a/test/ably/rest/authheaders_test.py +++ b/test/ably/http/authheaders_test.py @@ -2,7 +2,7 @@ import respx from httpx import Response -from ably import AblyRest +from ably.pubsub.server import create_http_client @pytest.fixture(scope='session', autouse=True) @@ -22,7 +22,7 @@ async def test_auth_headers_are_only_sent_to_auth_url(method, binary, override): options = {'auth_url': auth_url, 'auth_method': method, 'use_binary_protocol': binary} if not override: options['auth_headers'] = headers - client = AblyRest(**options) + client = create_http_client(**options) auth_route = respx.request(method, auth_url).mock(return_value=Response(200, json={ 'keyName': 'app.key', 'nonce': 'nonce', 'timestamp': 123456789, 'mac': 'signed-request', 'capability': '{"*":["*"]}', diff --git a/test/ably/rest/encoders_test.py b/test/ably/http/encoders_test.py similarity index 91% rename from test/ably/rest/encoders_test.py rename to test/ably/http/encoders_test.py index f8023c5d..24862161 100644 --- a/test/ably/rest/encoders_test.py +++ b/test/ably/http/encoders_test.py @@ -1,23 +1,18 @@ import base64 import json import logging -import sys from unittest import mock +from unittest.mock import AsyncMock import msgpack import pytest -from ably import CipherParams -from ably.types.message import Message -from ably.util.crypto import get_cipher +from ably.pubsub.server import CipherParams +from ably.pubsub.types.message import Message +from ably.pubsub.util.crypto import get_cipher from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase -if sys.version_info >= (3, 8): - from unittest.mock import AsyncMock -else: - from mock import AsyncMock - log = logging.getLogger(__name__) @@ -31,7 +26,7 @@ async def setup(self): async def test_text_utf8(self): channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foó') _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['data'] == 'foó' @@ -41,7 +36,7 @@ async def test_str(self): # This test only makes sense for py2 channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foo') _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['data'] == 'foo' @@ -50,7 +45,7 @@ async def test_str(self): async def test_with_binary_type(self): channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', bytearray(b'foo')) _, kwargs = post_mock.call_args raw_data = json.loads(kwargs['body'])['data'] @@ -60,7 +55,7 @@ async def test_with_binary_type(self): async def test_with_bytes_type(self): channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', b'foo') _, kwargs = post_mock.call_args raw_data = json.loads(kwargs['body'])['data'] @@ -70,7 +65,7 @@ async def test_with_bytes_type(self): async def test_with_json_dict_data(self): channel = self.ably.channels["persisted:publish"] data = {'foó': 'bár'} - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args raw_data = json.loads(json.loads(kwargs['body'])['data']) @@ -80,7 +75,7 @@ async def test_with_json_dict_data(self): async def test_with_json_list_data(self): channel = self.ably.channels["persisted:publish"] data = ['foó', 'bár'] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args raw_data = json.loads(json.loads(kwargs['body'])['data']) @@ -161,7 +156,7 @@ def decrypt(self, payload, options=None): async def test_text_utf8(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'fóo') _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['encoding'].strip('/') == 'utf-8/cipher+aes-128-cbc/base64' @@ -172,7 +167,7 @@ async def test_str(self): # This test only makes sense for py2 channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', 'foo') _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['data'] == 'foo' @@ -182,7 +177,7 @@ async def test_with_binary_type(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', bytearray(b'foo')) _, kwargs = post_mock.call_args @@ -195,7 +190,7 @@ async def test_with_json_dict_data(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) data = {'foó': 'bár'} - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['encoding'].strip('/') == 'json/utf-8/cipher+aes-128-cbc/base64' @@ -206,7 +201,7 @@ async def test_with_json_list_data(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) data = ['foó', 'bár'] - with mock.patch('ably.rest.rest.Http.post', new_callable=AsyncMock) as post_mock: + with mock.patch('ably.pubsub.http.http.Http.post', new_callable=AsyncMock) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args assert json.loads(kwargs['body'])['encoding'].strip('/') == 'json/utf-8/cipher+aes-128-cbc/base64' @@ -269,7 +264,7 @@ def decode(self, data): async def test_text_utf8(self): channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', 'foó') _, kwargs = post_mock.call_args @@ -279,7 +274,7 @@ async def test_text_utf8(self): async def test_with_binary_type(self): channel = self.ably.channels["persisted:publish"] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', bytearray(b'foo')) _, kwargs = post_mock.call_args @@ -289,7 +284,7 @@ async def test_with_binary_type(self): async def test_with_json_dict_data(self): channel = self.ably.channels["persisted:publish"] data = {'foó': 'bár'} - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args @@ -300,7 +295,7 @@ async def test_with_json_dict_data(self): async def test_with_json_list_data(self): channel = self.ably.channels["persisted:publish"] data = ['foó', 'bár'] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args @@ -367,7 +362,7 @@ def decode(self, data): async def test_text_utf8(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', 'fóo') _, kwargs = post_mock.call_args @@ -379,7 +374,7 @@ async def test_with_binary_type(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', bytearray(b'foo')) _, kwargs = post_mock.call_args @@ -393,7 +388,7 @@ async def test_with_json_dict_data(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) data = {'foó': 'bár'} - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args @@ -405,7 +400,7 @@ async def test_with_json_list_data(self): channel = self.ably.channels.get("persisted:publish_enc", cipher=self.cipher_params) data = ['foó', 'bár'] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish('event', data) _, kwargs = post_mock.call_args diff --git a/test/ably/rest/restannotations_test.py b/test/ably/http/httpannotations_test.py similarity index 97% rename from test/ably/rest/restannotations_test.py rename to test/ably/http/httpannotations_test.py index fcf2c696..3a8ca54b 100644 --- a/test/ably/rest/restannotations_test.py +++ b/test/ably/http/httpannotations_test.py @@ -4,9 +4,9 @@ import pytest -from ably import AblyException -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.message import Message +from ably.pubsub.server import AblyException +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.message import Message from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, assert_waiter @@ -14,7 +14,7 @@ @pytest.mark.parametrize("transport", ["json", "msgpack"], ids=["JSON", "MsgPack"]) -class TestRestAnnotations(BaseAsyncTestCase): +class TestHttpAnnotations(BaseAsyncTestCase): @pytest.fixture(autouse=True) async def setup(self, transport): diff --git a/test/ably/rest/restauth_test.py b/test/ably/http/httpauth_test.py similarity index 92% rename from test/ably/rest/restauth_test.py rename to test/ably/http/httpauth_test.py index 185021e1..3f793565 100644 --- a/test/ably/rest/restauth_test.py +++ b/test/ably/http/httpauth_test.py @@ -1,26 +1,21 @@ import base64 import logging -import sys import time import uuid from unittest import mock +from unittest.mock import AsyncMock from urllib.parse import parse_qs import pytest import respx from httpx import AsyncClient, Response -import ably -from ably import AblyAuthException, AblyRest, Auth -from ably.types.tokendetails import TokenDetails +import ably.pubsub.types.tokenrequest +from ably.pubsub.server import AblyAuthException, Auth, create_http_client +from ably.pubsub.types.tokendetails import TokenDetails from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol -if sys.version_info >= (3, 8): - from unittest.mock import AsyncMock -else: - from mock import AsyncMock - log = logging.getLogger(__name__) @@ -31,19 +26,19 @@ async def setup(self): self.test_vars = await TestApp.get_test_vars() def test_auth_init_key_only(self): - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"]) + ably = create_http_client(key=self.test_vars["keys"][0]["key_str"]) assert Auth.Method.BASIC == ably.auth.auth_mechanism, "Unexpected Auth method mismatch" assert ably.auth.auth_options.key_name == self.test_vars["keys"][0]['key_name'] assert ably.auth.auth_options.key_secret == self.test_vars["keys"][0]['key_secret'] def test_auth_init_token_only(self): - ably = AblyRest(token="this_is_not_really_a_token") + ably = create_http_client(token="this_is_not_really_a_token") assert Auth.Method.TOKEN == ably.auth.auth_mechanism, "Unexpected Auth method mismatch" def test_auth_token_details(self): td = TokenDetails() - ably = AblyRest(token_details=td) + ably = create_http_client(token_details=td) assert Auth.Method.TOKEN == ably.auth.auth_mechanism assert ably.auth.token_details is td @@ -69,7 +64,7 @@ def token_callback(token_params): assert Auth.Method.TOKEN == ably.auth.auth_mechanism, "Unexpected Auth method mismatch" def test_auth_init_with_key_and_client_id(self): - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"], client_id='testClientId') + ably = create_http_client(key=self.test_vars["keys"][0]["key_str"], client_id='testClientId') assert Auth.Method.BASIC == ably.auth.auth_mechanism, "Unexpected Auth method mismatch" assert ably.auth.client_id == 'testClientId' @@ -80,7 +75,7 @@ async def test_auth_init_with_token(self): # RSA11 async def test_request_basic_auth_header(self): - ably = AblyRest(key_secret='foo', key_name='bar') + ably = create_http_client(key_secret='foo', key_name='bar') with mock.patch.object(AsyncClient, 'send') as get_mock: get_mock.return_value = {"status": 200, "headers": {}} @@ -94,7 +89,7 @@ async def test_request_basic_auth_header(self): # RSA7e2 async def test_request_basic_auth_header_with_client_id(self): - ably = AblyRest(key_secret='foo', key_name='bar', client_id='client_id') + ably = create_http_client(key_secret='foo', key_name='bar', client_id='client_id') with mock.patch.object(AsyncClient, 'send') as get_mock: get_mock.return_value = {"status": 200, "headers": {}} @@ -107,7 +102,7 @@ async def test_request_basic_auth_header_with_client_id(self): assert client_id == base64.b64encode('client_id'.encode('ascii')).decode('utf-8') async def test_request_token_auth_header(self): - ably = AblyRest(token='not_a_real_token') + ably = create_http_client(token='not_a_real_token') with mock.patch.object(AsyncClient, 'send') as get_mock: get_mock.return_value = {"status": 200, "headers": {}} @@ -122,26 +117,27 @@ async def test_request_token_auth_header(self): def test_if_cant_authenticate_via_token(self): with pytest.raises(ValueError): - AblyRest(use_token_auth=True) + create_http_client(use_token_auth=True) def test_use_auth_token(self): - ably = AblyRest(use_token_auth=True, key=self.test_vars["keys"][0]["key_str"]) + ably = create_http_client(use_token_auth=True, key=self.test_vars["keys"][0]["key_str"]) assert ably.auth.auth_mechanism == Auth.Method.TOKEN def test_with_client_id(self): - ably = AblyRest(use_token_auth=True, client_id='client_id', key=self.test_vars["keys"][0]["key_str"]) + ably = create_http_client(use_token_auth=True, client_id='client_id', + key=self.test_vars["keys"][0]["key_str"]) assert ably.auth.auth_mechanism == Auth.Method.TOKEN def test_with_auth_url(self): - ably = AblyRest(auth_url='auth_url') + ably = create_http_client(auth_url='auth_url') assert ably.auth.auth_mechanism == Auth.Method.TOKEN def test_with_auth_callback(self): - ably = AblyRest(auth_callback=lambda x: x) + ably = create_http_client(auth_callback=lambda x: x) assert ably.auth.auth_mechanism == Auth.Method.TOKEN def test_with_token(self): - ably = AblyRest(token='a token') + ably = create_http_client(token='a token') assert ably.auth.auth_mechanism == Auth.Method.TOKEN def test_default_ttl_is_1hour(self): @@ -149,19 +145,19 @@ def test_default_ttl_is_1hour(self): assert TokenDetails.DEFAULTS['ttl'] == one_hour_in_ms def test_with_auth_method(self): - ably = AblyRest(token='a token', auth_method='POST') + ably = create_http_client(token='a token', auth_method='POST') assert ably.auth.auth_options.auth_method == 'POST' def test_with_auth_headers(self): - ably = AblyRest(token='a token', auth_headers={'h1': 'v1'}) + ably = create_http_client(token='a token', auth_headers={'h1': 'v1'}) assert ably.auth.auth_options.auth_headers == {'h1': 'v1'} def test_with_auth_params(self): - ably = AblyRest(token='a token', auth_params={'p': 'v'}) + ably = create_http_client(token='a token', auth_params={'p': 'v'}) assert ably.auth.auth_options.auth_params == {'p': 'v'} def test_with_default_token_params(self): - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"], + ably = create_http_client(key=self.test_vars["keys"][0]["key_str"], default_token_params={'ttl': 12345}) assert ably.auth.auth_options.default_token_params == {'ttl': 12345} @@ -198,7 +194,7 @@ async def test_authorize_always_creates_new_token(self): async def test_authorize_create_new_token_if_expired(self): token = await self.ably.auth.authorize() - with mock.patch('ably.rest.auth.Auth.token_details_has_expired', + with mock.patch('ably.pubsub.http.auth.Auth.token_details_has_expired', return_value=True): new_token = await self.ably.auth.authorize() @@ -212,7 +208,7 @@ async def test_authorize_returns_a_token_details(self): async def test_authorize_adheres_to_request_token(self): token_params = {'ttl': 10, 'client_id': 'client_id'} auth_params = {'auth_url': 'somewhere.com', 'query_time': True} - with mock.patch('ably.rest.auth.Auth.request_token', new_callable=AsyncMock) as request_mock: + with mock.patch('ably.pubsub.http.auth.Auth.request_token', new_callable=AsyncMock) as request_mock: await self.ably.auth.authorize(token_params, auth_params) token_called, auth_called = request_mock.call_args @@ -251,7 +247,7 @@ async def test_if_parameters_are_stored_and_used_as_defaults(self): auth_options = dict(self.ably.auth.auth_options.auth_options) auth_options['auth_headers'] = {'a_headers': 'a_value'} await self.ably.auth.authorize({'ttl': 555}, auth_options) - with mock.patch('ably.rest.auth.Auth.request_token', + with mock.patch('ably.pubsub.http.auth.Auth.request_token', wraps=self.ably.auth.request_token) as request_mock: await self.ably.auth.authorize() @@ -263,7 +259,7 @@ async def test_if_parameters_are_stored_and_used_as_defaults(self): auth_options = dict(self.ably.auth.auth_options.auth_options) auth_options['auth_headers'] = None await self.ably.auth.authorize({}, auth_options) - with mock.patch('ably.rest.auth.Auth.request_token', + with mock.patch('ably.pubsub.http.auth.Auth.request_token', wraps=self.ably.auth.request_token) as request_mock: await self.ably.auth.authorize() @@ -283,8 +279,8 @@ async def test_timestamp_is_not_stored(self): # call authorize again with timestamp set timestamp = await self.ably.time() - with mock.patch('ably.rest.auth.TokenRequest', - wraps=ably.types.tokenrequest.TokenRequest) as tr_mock: + with mock.patch('ably.pubsub.http.auth.TokenRequest', + wraps=ably.pubsub.types.tokenrequest.TokenRequest) as tr_mock: auth_options = dict(self.ably.auth.auth_options.auth_options) auth_options['auth_headers'] = {'a_headers': 'a_value'} token_2 = await self.ably.auth.authorize( @@ -295,8 +291,8 @@ async def test_timestamp_is_not_stored(self): assert tr_mock.call_args[1]['timestamp'] == timestamp # call authorize again with no params - with mock.patch('ably.rest.auth.TokenRequest', - wraps=ably.types.tokenrequest.TokenRequest) as tr_mock: + with mock.patch('ably.pubsub.http.auth.TokenRequest', + wraps=ably.pubsub.types.tokenrequest.TokenRequest) as tr_mock: token_4 = await self.ably.auth.authorize() assert isinstance(token_4, TokenDetails) assert token_2 != token_4 diff --git a/test/ably/rest/restcapability_test.py b/test/ably/http/httpcapability_test.py similarity index 98% rename from test/ably/rest/restcapability_test.py rename to test/ably/http/httpcapability_test.py index c95c651d..7e96a57d 100644 --- a/test/ably/rest/restcapability_test.py +++ b/test/ably/http/httpcapability_test.py @@ -1,12 +1,12 @@ import pytest -from ably.types.capability import Capability -from ably.util.exceptions import AblyException +from ably.pubsub.types.capability import Capability +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol -class TestRestCapability(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpCapability(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): diff --git a/test/ably/rest/restchannelhistory_test.py b/test/ably/http/httpchannelhistory_test.py similarity index 98% rename from test/ably/rest/restchannelhistory_test.py rename to test/ably/http/httpchannelhistory_test.py index 2b15ae0d..e5773172 100644 --- a/test/ably/rest/restchannelhistory_test.py +++ b/test/ably/http/httpchannelhistory_test.py @@ -3,15 +3,15 @@ import pytest import respx -from ably import AblyException -from ably.http.paginatedresult import PaginatedResult +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.server import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol log = logging.getLogger(__name__) -class TestRestChannelHistory(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpChannelHistory(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): diff --git a/test/ably/rest/restchannelmutablemessages_test.py b/test/ably/http/httpchannelmutablemessages_test.py similarity index 98% rename from test/ably/rest/restchannelmutablemessages_test.py rename to test/ably/http/httpchannelmutablemessages_test.py index b4f32ef4..e965011c 100644 --- a/test/ably/rest/restchannelmutablemessages_test.py +++ b/test/ably/http/httpchannelmutablemessages_test.py @@ -3,9 +3,9 @@ import pytest -from ably import AblyException, CipherParams, MessageAction -from ably.types.message import Message -from ably.types.operations import MessageOperation +from ably.pubsub.server import AblyException, CipherParams, MessageAction +from ably.pubsub.types.message import Message +from ably.pubsub.types.operations import MessageOperation from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, assert_waiter @@ -13,7 +13,7 @@ @pytest.mark.parametrize("transport", ["json", "msgpack"], ids=["JSON", "MsgPack"]) -class TestRestChannelMutableMessages(BaseAsyncTestCase): +class TestHttpChannelMutableMessages(BaseAsyncTestCase): @pytest.fixture(autouse=True) async def setup(self, transport): diff --git a/test/ably/rest/restchannelpublish_test.py b/test/ably/http/httpchannelpublish_test.py similarity index 97% rename from test/ably/rest/restchannelpublish_test.py rename to test/ably/http/httpchannelpublish_test.py index 5165a00e..1edff7be 100644 --- a/test/ably/rest/restchannelpublish_test.py +++ b/test/ably/http/httpchannelpublish_test.py @@ -10,11 +10,11 @@ import msgpack import pytest -from ably import AblyException, IncompatibleClientIdException, api_version -from ably.rest.auth import Auth -from ably.types.message import Message -from ably.types.tokendetails import TokenDetails -from ably.util import case +from ably.pubsub.http.auth import Auth +from ably.pubsub.server import AblyException, IncompatibleClientIdException, api_version +from ably.pubsub.types.message import Message +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util import case from test.ably import utils from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, assert_waiter, dont_vary_protocol @@ -24,7 +24,7 @@ # Ignore library warning regarding client_id @pytest.mark.filterwarnings('ignore::DeprecationWarning') -class TestRestChannelPublish(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpChannelPublish(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): @@ -103,7 +103,7 @@ async def test_message_list_generate_one_request(self): expected_messages = [Message(f"name-{i}", str(i)) for i in range(3)] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish(messages=expected_messages) assert post_mock.call_count == 1 @@ -184,7 +184,7 @@ async def test_publish_message_null_name_and_data_keys_arent_sent(self): channel = self.ably.channels[ self.get_channel_name('persisted:null_name_and_data_keys_arent_sent_channel')] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish(name=None, data=None) @@ -244,7 +244,7 @@ async def test_publish_message_without_client_id_on_identified_client(self): channel = self.ably_with_client_id.channels[ self.get_channel_name('persisted:no_client_id_identified_client')] - with mock.patch('ably.rest.rest.Http.post', + with mock.patch('ably.pubsub.http.http.Http.post', wraps=channel.ably.http.post) as post_mock: await channel.publish(name='publish', data='test') @@ -446,7 +446,7 @@ async def test_publish_params(self): assert 40099 == excinfo.value.code -class TestRestChannelPublishIdempotent(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpChannelPublishIdempotent(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): diff --git a/test/ably/rest/restchannels_test.py b/test/ably/http/httpchannels_test.py similarity index 95% rename from test/ably/rest/restchannels_test.py rename to test/ably/http/httpchannels_test.py index b5e59957..255b23e9 100644 --- a/test/ably/rest/restchannels_test.py +++ b/test/ably/http/httpchannels_test.py @@ -2,9 +2,9 @@ import pytest -from ably import AblyException -from ably.rest.channel import Channel, Channels, Presence -from ably.util.crypto import generate_random_key +from ably.pubsub.http.channel import Channel, Channels, Presence +from ably.pubsub.server import AblyException +from ably.pubsub.util.crypto import generate_random_key from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/rest/restchannelstatus_test.py b/test/ably/http/httpchannelstatus_test.py similarity index 97% rename from test/ably/rest/restchannelstatus_test.py rename to test/ably/http/httpchannelstatus_test.py index cb455362..cd027037 100644 --- a/test/ably/rest/restchannelstatus_test.py +++ b/test/ably/http/httpchannelstatus_test.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) -class TestRestChannelStatus(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpChannelStatus(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): diff --git a/test/ably/rest/restcrypto_test.py b/test/ably/http/httpcrypto_test.py similarity index 97% rename from test/ably/rest/restcrypto_test.py rename to test/ably/http/httpcrypto_test.py index 94812b29..77b63250 100644 --- a/test/ably/rest/restcrypto_test.py +++ b/test/ably/http/httpcrypto_test.py @@ -6,9 +6,9 @@ import pytest from Crypto import Random -from ably import AblyException -from ably.types.message import Message -from ably.util.crypto import CipherParams, generate_random_key, get_cipher, get_default_params +from ably.pubsub.server import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.util.crypto import CipherParams, generate_random_key, get_cipher, get_default_params from test.ably import utils from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, BaseTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) -class TestRestCrypto(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpCrypto(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): diff --git a/test/ably/rest/resthttp_test.py b/test/ably/http/httphttp_test.py similarity index 91% rename from test/ably/rest/resthttp_test.py rename to test/ably/http/httphttp_test.py index 385d63ec..e89fb1c8 100644 --- a/test/ably/rest/resthttp_test.py +++ b/test/ably/http/httphttp_test.py @@ -9,16 +9,16 @@ import respx from httpx import Response -from ably import AblyRest -from ably.http.httputils import HttpUtils -from ably.transport.defaults import Defaults -from ably.types.options import Options -from ably.util.exceptions import AblyException +from ably.pubsub.request.httputils import HttpUtils +from ably.pubsub.server import create_http_client +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.types.options import Options +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase -class TestRestHttp(BaseAsyncTestCase): +class TestHttpHttp(BaseAsyncTestCase): async def test_host_for_url_brackets_only_ipv6_literals(self): assert HttpUtils.host_for_url('::1') == '[::1]' assert HttpUtils.host_for_url('2001:db8::1') == '[2001:db8::1]' @@ -29,7 +29,7 @@ async def test_host_for_url_brackets_only_ipv6_literals(self): assert HttpUtils.host_for_url('[::1]') == '[::1]' async def test_max_retry_attempts_and_timeouts_defaults(self): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") assert 'http_open_timeout' in ably.http.CONNECTION_RETRY_DEFAULTS assert 'http_request_timeout' in ably.http.CONNECTION_RETRY_DEFAULTS @@ -42,7 +42,7 @@ async def test_max_retry_attempts_and_timeouts_defaults(self): await ably.close() async def test_cumulative_timeout(self): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") assert 'http_max_retry_duration' in ably.http.CONNECTION_RETRY_DEFAULTS ably.options.http_max_retry_duration = 0.5 @@ -59,7 +59,7 @@ def sleep_and_raise(*args, **kwargs): await ably.close() async def test_host_fallback(self): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") def make_url(host): base_url = f"{ably.http.preferred_scheme}://{host}:{ably.http.preferred_port}" @@ -89,7 +89,7 @@ def make_url(host): @respx.mock async def test_no_host_fallback_nor_retries_if_custom_host(self): custom_host = 'example.org' - ably = AblyRest(token="foo", endpoint=custom_host) + ably = create_http_client(token="foo", endpoint=custom_host) mock_route = respx.get("https://example.org").mock(side_effect=httpx.RequestError('')) @@ -139,7 +139,7 @@ async def side_effect(*args, **kwargs): @respx.mock async def test_no_retry_if_not_500_to_599_http_code(self): default_host = Options().get_host() - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") default_url = f"{ably.http.preferred_scheme}://{default_host}:{ably.http.preferred_port}/" @@ -162,7 +162,7 @@ async def test_500_errors(self): https://github.com/ably/ably-python/issues/160 """ - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") mock_request = respx.route().mock(return_value=httpx.Response(500, text="Internal Server Error")) @@ -174,7 +174,7 @@ async def test_500_errors(self): await ably.close() def test_custom_http_timeouts(self): - ably = AblyRest( + ably = create_http_client( token="foo", http_request_timeout=30, http_open_timeout=8, http_max_retry_count=6, http_max_retry_duration=20) @@ -194,7 +194,7 @@ async def test_request_headers(self): # Agent assert 'Ably-Agent' in r.request.headers - expr = r"^ably-python\/\d+.\d+.\d+(-beta\.\d+)? python\/\d.\d+.\d+$" + expr = r"^ably-pubsub-python\/\d+.\d+.\d+(-beta\.\d+)? python\/\d.\d+.\d+$" assert re.search(expr, r.request.headers['Ably-Agent']) await ably.close() diff --git a/test/ably/rest/restinit_test.py b/test/ably/http/httpinit_test.py similarity index 74% rename from test/ably/rest/restinit_test.py rename to test/ably/http/httpinit_test.py index 25e7c5af..b8202a3b 100644 --- a/test/ably/rest/restinit_test.py +++ b/test/ably/http/httpinit_test.py @@ -3,14 +3,14 @@ import pytest from httpx import AsyncClient -from ably import AblyException, AblyRest -from ably.transport.defaults import Defaults -from ably.types.tokendetails import TokenDetails +from ably.pubsub.server import AblyException, create_http_client +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.types.tokendetails import TokenDetails from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol -class TestRestInit(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpInit(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): @@ -18,7 +18,7 @@ async def setup(self): @dont_vary_protocol def test_key_only(self): - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"]) + ably = create_http_client(key=self.test_vars["keys"][0]["key_str"]) assert ably.options.key_name == self.test_vars["keys"][0]["key_name"], "Key name does not match" assert ably.options.key_secret == self.test_vars["keys"][0]["key_secret"], "Key secret does not match" @@ -27,65 +27,65 @@ def per_protocol_setup(self, use_binary_protocol): @dont_vary_protocol def test_with_token(self): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") assert ably.options.auth_token == "foo", "Token not set at options" @dont_vary_protocol def test_with_token_details(self): td = TokenDetails() - ably = AblyRest(token_details=td) + ably = create_http_client(token_details=td) assert ably.options.token_details is td @dont_vary_protocol def test_with_options_token_callback(self): def token_callback(**params): return "this_is_not_really_a_token_request" - AblyRest(auth_callback=token_callback) + create_http_client(auth_callback=token_callback) @dont_vary_protocol def test_ambiguous_key_raises_value_error(self): with pytest.raises(ValueError, match="mutually exclusive"): - AblyRest(key=self.test_vars["keys"][0]["key_str"], key_name='x') + create_http_client(key=self.test_vars["keys"][0]["key_str"], key_name='x') with pytest.raises(ValueError, match="mutually exclusive"): - AblyRest(key=self.test_vars["keys"][0]["key_str"], key_secret='x') + create_http_client(key=self.test_vars["keys"][0]["key_str"], key_secret='x') @dont_vary_protocol def test_with_key_name_or_secret_only(self): with pytest.raises(ValueError, match="key is missing"): - AblyRest(key_name='x') + create_http_client(key_name='x') with pytest.raises(ValueError, match="key is missing"): - AblyRest(key_secret='x') + create_http_client(key_secret='x') @dont_vary_protocol def test_with_key_name_and_secret(self): - ably = AblyRest(key_name="foo", key_secret="bar") + ably = create_http_client(key_name="foo", key_secret="bar") assert ably.options.key_name == "foo", "Key name does not match" assert ably.options.key_secret == "bar", "Key secret does not match" @dont_vary_protocol def test_with_options_auth_url(self): - AblyRest(auth_url='not_really_an_url') + create_http_client(auth_url='not_really_an_url') # RSC11 @dont_vary_protocol def test_rest_host_and_environment(self): # endpoint host - ably = AblyRest(token='foo', endpoint="some.other.host") + ably = create_http_client(token='foo', endpoint="some.other.host") assert "some.other.host" == ably.options.get_host(), "Unexpected host mismatch" # endpoint: main - ably = AblyRest(token='foo', endpoint="main") + ably = create_http_client(token='foo', endpoint="main") host = ably.options.get_host() assert "main.realtime.ably.net" == host, f"Unexpected host mismatch {host}" # endpoint: other - ably = AblyRest(token='foo', endpoint="nonprod:sandbox") + ably = create_http_client(token='foo', endpoint="nonprod:sandbox") host = ably.options.get_host() assert "sandbox.realtime.ably-nonprod.net" == host, f"Unexpected host mismatch {host}" # both, as per #TO3k2 with pytest.raises(AblyException): - ably = AblyRest(token='foo', rest_host="some.other.host", + ably = create_http_client(token='foo', rest_host="some.other.host", endpoint="some.other.environment") # RSC15 @@ -99,68 +99,68 @@ def test_fallback_hosts(self): # Fallback hosts specified (RSC15g1) for aux in fallback_hosts: - ably = AblyRest(token='foo', fallback_hosts=aux) + ably = create_http_client(token='foo', fallback_hosts=aux) assert sorted(aux) == sorted(ably.options.get_fallback_hosts()) # Specify endpoint (RSC15g2) - ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10) + ably = create_http_client(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10) assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted( ably.options.get_fallback_hosts()) # Fallback hosts and endpoint not specified (RSC15g3) - ably = AblyRest(token='foo', http_max_retry_count=10) + ably = create_http_client(token='foo', http_max_retry_count=10) assert sorted(Defaults.get_fallback_hosts()) == sorted(ably.options.get_fallback_hosts()) # RSC15f - ably = AblyRest(token='foo') + ably = create_http_client(token='foo') assert 600000 == ably.options.fallback_retry_timeout - ably = AblyRest(token='foo', fallback_retry_timeout=1000) + ably = create_http_client(token='foo', fallback_retry_timeout=1000) assert 1000 == ably.options.fallback_retry_timeout @dont_vary_protocol def test_specified_host(self): - ably = AblyRest(token='foo', endpoint="some.other.host") + ably = create_http_client(token='foo', endpoint="some.other.host") assert "some.other.host" == ably.options.get_host(), "Unexpected host mismatch" @dont_vary_protocol def test_specified_port(self): - ably = AblyRest(token='foo', port=9998, tls_port=9999) + ably = create_http_client(token='foo', port=9998, tls_port=9999) assert 9999 == Defaults.get_port(ably.options),\ f"Unexpected port mismatch. Expected: 9999. Actual: {ably.options.tls_port}" @dont_vary_protocol def test_specified_non_tls_port(self): - ably = AblyRest(token='foo', port=9998, tls=False) + ably = create_http_client(token='foo', port=9998, tls=False) assert 9998 == Defaults.get_port(ably.options),\ f"Unexpected port mismatch. Expected: 9999. Actual: {ably.options.tls_port}" @dont_vary_protocol def test_specified_tls_port(self): - ably = AblyRest(token='foo', tls_port=9999, tls=True) + ably = create_http_client(token='foo', tls_port=9999, tls=True) assert 9999 == Defaults.get_port(ably.options),\ f"Unexpected port mismatch. Expected: 9999. Actual: {ably.options.tls_port}" @dont_vary_protocol def test_tls_defaults_to_true(self): - ably = AblyRest(token='foo') + ably = create_http_client(token='foo') assert ably.options.tls, "Expected encryption to default to true" assert Defaults.tls_port == Defaults.get_port(ably.options), "Unexpected port mismatch" @dont_vary_protocol def test_tls_can_be_disabled(self): - ably = AblyRest(token='foo', tls=False) + ably = create_http_client(token='foo', tls=False) assert not ably.options.tls, "Expected encryption to be False" assert Defaults.port == Defaults.get_port(ably.options), "Unexpected port mismatch" @dont_vary_protocol def test_with_no_params(self): with pytest.raises(ValueError): - AblyRest() + create_http_client() @dont_vary_protocol def test_with_no_auth_params(self): with pytest.raises(ValueError): - AblyRest(port=111) + create_http_client(port=111) # RSA10k async def test_query_time_param(self): @@ -168,8 +168,8 @@ async def test_query_time_param(self): use_binary_protocol=self.use_binary_protocol) timestamp = ably.auth._timestamp - with patch('ably.rest.rest.AblyRest.time', wraps=ably.time) as server_time,\ - patch('ably.rest.auth.Auth._timestamp', wraps=timestamp) as local_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=ably.time) as server_time,\ + patch('ably.pubsub.http.auth.Auth._timestamp', wraps=timestamp) as local_time: await ably.auth.request_token() assert local_time.call_count == 1 assert server_time.call_count == 1 @@ -181,19 +181,19 @@ async def test_query_time_param(self): @dont_vary_protocol def test_requests_over_https_production(self): - ably = AblyRest(token='token') + ably = create_http_client(token='token') assert 'https://main.realtime.ably.net' == f'{ably.http.preferred_scheme}://{ably.http.preferred_host}' assert ably.http.preferred_port == 443 @dont_vary_protocol def test_requests_over_http_production(self): - ably = AblyRest(token='token', tls=False) + ably = create_http_client(token='token', tls=False) assert 'http://main.realtime.ably.net' == f'{ably.http.preferred_scheme}://{ ably.http.preferred_host}' assert ably.http.preferred_port == 80 @dont_vary_protocol async def test_request_basic_auth_over_http_fails(self): - ably = AblyRest(key_secret='foo', key_name='bar', tls=False) + ably = create_http_client(key_secret='foo', key_name='bar', tls=False) with pytest.raises(AblyException) as excinfo: await ably.http.get('/time', skip_auth=False) @@ -204,7 +204,7 @@ async def test_request_basic_auth_over_http_fails(self): @dont_vary_protocol async def test_environment(self): - ably = AblyRest(token='token', endpoint='custom') + ably = create_http_client(token='token', endpoint='custom') with patch.object(AsyncClient, 'send', wraps=ably.http._Http__client.send) as get_mock: try: await ably.time() @@ -217,7 +217,7 @@ async def test_environment(self): @dont_vary_protocol def test_accepts_custom_http_timeouts(self): - ably = AblyRest( + ably = create_http_client( token="foo", http_request_timeout=30, http_open_timeout=8, http_max_retry_count=6, http_max_retry_duration=20) diff --git a/test/ably/rest/restpaginatedresult_test.py b/test/ably/http/httppaginatedresult_test.py similarity index 98% rename from test/ably/rest/restpaginatedresult_test.py rename to test/ably/http/httppaginatedresult_test.py index 9aa85689..dd33a071 100644 --- a/test/ably/rest/restpaginatedresult_test.py +++ b/test/ably/http/httppaginatedresult_test.py @@ -2,7 +2,7 @@ import respx from httpx import Response -from ably.http.paginatedresult import PaginatedResult +from ably.pubsub.request.paginatedresult import PaginatedResult from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/rest/restpresence_test.py b/test/ably/http/httppresence_test.py similarity index 98% rename from test/ably/rest/restpresence_test.py rename to test/ably/http/httppresence_test.py index 8767b0c6..1fa52d42 100644 --- a/test/ably/rest/restpresence_test.py +++ b/test/ably/http/httppresence_test.py @@ -3,8 +3,8 @@ import pytest import respx -from ably.http.paginatedresult import PaginatedResult -from ably.types.presence import PresenceMessage +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.presence import PresenceMessage from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol diff --git a/test/ably/rest/restpush_test.py b/test/ably/http/httppush_test.py similarity index 98% rename from test/ably/rest/restpush_test.py rename to test/ably/http/httppush_test.py index 30c4798d..9c430cf7 100644 --- a/test/ably/rest/restpush_test.py +++ b/test/ably/http/httppush_test.py @@ -5,8 +5,8 @@ import pytest -from ably import AblyAuthException, AblyException, DeviceDetails, PushChannelSubscription -from ably.http.paginatedresult import PaginatedResult +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.server import AblyAuthException, AblyException, DeviceDetails, PushChannelSubscription from test.ably.testapp import TestApp from test.ably.utils import ( BaseAsyncTestCase, diff --git a/test/ably/rest/restrequest_test.py b/test/ably/http/httprequest_test.py similarity index 96% rename from test/ably/rest/restrequest_test.py rename to test/ably/http/httprequest_test.py index 63c20ccf..ec41c5ff 100644 --- a/test/ably/rest/restrequest_test.py +++ b/test/ably/http/httprequest_test.py @@ -2,15 +2,15 @@ import pytest import respx -from ably import AblyRest -from ably.http.paginatedresult import HttpPaginatedResponse -from ably.transport.defaults import Defaults +from ably.pubsub.request.paginatedresult import HttpPaginatedResponse +from ably.pubsub.server import create_http_client +from ably.pubsub.transport.defaults import Defaults from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol # RSC19 -class TestRestRequest(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpRequest(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @pytest.fixture(autouse=True) async def setup(self): @@ -98,7 +98,7 @@ async def test_headers(self): async def test_timeout(self): # Timeout timeout = 0.000001 - ably = AblyRest(token="foo", http_request_timeout=timeout) + ably = create_http_client(token="foo", http_request_timeout=timeout) assert ably.http.http_request_timeout == timeout # `http_open_timeout` keeps its four-second default, so the request carries a # connect budget as well as a read budget and either can be the one to expire @@ -122,7 +122,7 @@ async def test_timeout(self): await ably.close() # Bad host, no Fallback - ably = AblyRest(key=self.test_vars["keys"][0]["key_str"], + ably = create_http_client(key=self.test_vars["keys"][0]["key_str"], endpoint='some.other.host', port=self.test_vars["port"], tls_port=self.test_vars["tls_port"], diff --git a/test/ably/rest/reststats_test.py b/test/ably/http/httpstats_test.py similarity index 94% rename from test/ably/rest/reststats_test.py rename to test/ably/http/httpstats_test.py index cef28817..5f039058 100644 --- a/test/ably/rest/reststats_test.py +++ b/test/ably/http/httpstats_test.py @@ -3,16 +3,16 @@ import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.types.stats import Stats -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.stats import Stats +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol log = logging.getLogger(__name__) -class TestRestAppStatsSetup: +class TestHttpAppStatsSetup: __stats_added = False def get_params(self): @@ -68,9 +68,9 @@ async def setup(self): } ) # asynctest does not support setUpClass method - if not TestRestAppStatsSetup.__stats_added: + if not TestHttpAppStatsSetup.__stats_added: await self.ably.http.post('/stats', body=stats + previous_stats) - TestRestAppStatsSetup.__stats_added = True + TestHttpAppStatsSetup.__stats_added = True yield await self.ably.close() await self.ably_text.close() @@ -79,7 +79,7 @@ def per_protocol_setup(self, use_binary_protocol): self.ably.options.use_binary_protocol = use_binary_protocol -class TestDirectionForwards(TestRestAppStatsSetup, BaseAsyncTestCase, +class TestDirectionForwards(TestHttpAppStatsSetup, BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): def get_params(self): @@ -105,7 +105,7 @@ async def test_three_pages(self): assert page3.items[0].entries["messages.inbound.realtime.all.count"] == 70 -class TestDirectionBackwards(TestRestAppStatsSetup, BaseAsyncTestCase, +class TestDirectionBackwards(TestHttpAppStatsSetup, BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): def get_params(self): @@ -131,7 +131,7 @@ async def test_three_pages(self): assert page3.items[0].entries["messages.inbound.realtime.all.count"] == 50 -class TestOnlyLastYear(TestRestAppStatsSetup, BaseAsyncTestCase, +class TestOnlyLastYear(TestHttpAppStatsSetup, BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): def get_params(self): @@ -148,7 +148,7 @@ async def test_default_is_backwards(self): assert stats[-1].entries["messages.inbound.realtime.messages.count"] == 50 -class TestPreviousYear(TestRestAppStatsSetup, BaseAsyncTestCase, +class TestPreviousYear(TestHttpAppStatsSetup, BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): def get_params(self): @@ -165,7 +165,7 @@ async def test_default_100_pagination(self): assert len(next_page.items) == 20 -class TestRestAppStats(TestRestAppStatsSetup, BaseAsyncTestCase, +class TestHttpAppStats(TestHttpAppStatsSetup, BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): @dont_vary_protocol diff --git a/test/ably/rest/resttime_test.py b/test/ably/http/httptime_test.py similarity index 92% rename from test/ably/rest/resttime_test.py rename to test/ably/http/httptime_test.py index 4b78620a..5dbeaee2 100644 --- a/test/ably/rest/resttime_test.py +++ b/test/ably/http/httptime_test.py @@ -2,12 +2,12 @@ import pytest -from ably import AblyException +from ably.pubsub.server import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol -class TestRestTime(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpTime(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): def per_protocol_setup(self, use_binary_protocol): self.ably.options.use_binary_protocol = use_binary_protocol diff --git a/test/ably/rest/resttoken_test.py b/test/ably/http/httptoken_test.py similarity index 91% rename from test/ably/rest/resttoken_test.py rename to test/ably/http/httptoken_test.py index 5052f1be..2406f120 100644 --- a/test/ably/rest/resttoken_test.py +++ b/test/ably/http/httptoken_test.py @@ -5,16 +5,16 @@ import pytest -from ably import AblyException, AblyRest, Capability -from ably.types.tokendetails import TokenDetails -from ably.types.tokenrequest import TokenRequest +from ably.pubsub.server import AblyException, Capability, create_http_client +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.types.tokenrequest import TokenRequest from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, VaryByProtocolTestsMetaclass, dont_vary_protocol log = logging.getLogger(__name__) -class TestRestToken(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): +class TestHttpToken(BaseAsyncTestCase, metaclass=VaryByProtocolTestsMetaclass): async def server_time(self): return await self.ably.time() @@ -120,8 +120,8 @@ async def test_token_generation_with_invalid_ttl(self): async def test_token_generation_with_local_time(self): timestamp = self.ably.auth._timestamp - with patch('ably.rest.rest.AblyRest.time', wraps=self.ably.time) as server_time,\ - patch('ably.rest.auth.Auth._timestamp', wraps=timestamp) as local_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=self.ably.time) as server_time,\ + patch('ably.pubsub.http.auth.Auth._timestamp', wraps=timestamp) as local_time: await self.ably.auth.request_token() assert local_time.called assert not server_time.called @@ -129,8 +129,8 @@ async def test_token_generation_with_local_time(self): # RSA10k async def test_token_generation_with_server_time(self): timestamp = self.ably.auth._timestamp - with patch('ably.rest.rest.AblyRest.time', wraps=self.ably.time) as server_time,\ - patch('ably.rest.auth.Auth._timestamp', wraps=timestamp) as local_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=self.ably.time) as server_time,\ + patch('ably.pubsub.http.auth.Auth._timestamp', wraps=timestamp) as local_time: await self.ably.auth.request_token(query_time=True) assert local_time.call_count == 1 assert server_time.call_count == 1 @@ -182,8 +182,8 @@ async def test_key_name_and_secret_are_required(self): @dont_vary_protocol async def test_with_local_time(self): timestamp = self.ably.auth._timestamp - with patch('ably.rest.rest.AblyRest.time', wraps=self.ably.time) as server_time,\ - patch('ably.rest.auth.Auth._timestamp', wraps=timestamp) as local_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=self.ably.time) as server_time,\ + patch('ably.pubsub.http.auth.Auth._timestamp', wraps=timestamp) as local_time: await self.ably.auth.create_token_request( key_name=self.key_name, key_secret=self.key_secret, query_time=False) assert local_time.called @@ -193,8 +193,8 @@ async def test_with_local_time(self): @dont_vary_protocol async def test_with_server_time(self): timestamp = self.ably.auth._timestamp - with patch('ably.rest.rest.AblyRest.time', wraps=self.ably.time) as server_time,\ - patch('ably.rest.auth.Auth._timestamp', wraps=timestamp) as local_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=self.ably.time) as server_time,\ + patch('ably.pubsub.http.auth.Auth._timestamp', wraps=timestamp) as local_time: await self.ably.auth.create_token_request( key_name=self.key_name, key_secret=self.key_secret, query_time=True) assert local_time.call_count == 1 @@ -314,7 +314,7 @@ async def auth_callback(token_params): @dont_vary_protocol async def test_hmac(self): - ably = AblyRest(key_name='a_key_name', key_secret='a_secret') + ably = create_http_client(key_name='a_key_name', key_secret='a_secret') token_params = { 'ttl': 1000, 'nonce': 'abcde100', @@ -329,7 +329,7 @@ async def test_hmac(self): # AO2g @dont_vary_protocol async def test_query_server_time(self): - with patch('ably.rest.rest.AblyRest.time', wraps=self.ably.time) as server_time: + with patch('ably.pubsub.http.http.DefaultPubSubHttpClient.time', wraps=self.ably.time) as server_time: await self.ably.auth.create_token_request( key_name=self.key_name, key_secret=self.key_secret, query_time=True) assert server_time.call_count == 1 diff --git a/test/ably/realtime/deltadecoding_test.py b/test/ably/realtime/deltadecoding_test.py index f2930dfb..b067023e 100644 --- a/test/ably/realtime/deltadecoding_test.py +++ b/test/ably/realtime/deltadecoding_test.py @@ -9,12 +9,12 @@ import pytest -from ably import AblyRealtime -from ably.types.channelstate import ChannelState -from ably.types.message import Message -from ably.types.mixins import DecodingContext -from ably.types.options import VCDiffDecoder -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_realtime_client +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.message import Message +from ably.pubsub.types.mixins import DecodingContext +from ably.pubsub.types.options import VCDiffDecoder +from ably.pubsub.util.exceptions import AblyException from test.ably.utils import BaseAsyncTestCase @@ -131,7 +131,7 @@ class TestChannelDeltaBatch(BaseAsyncTestCase): """RealtimeChannel handling of a protocol message carrying several deltas""" def setup_channel(self, decoder): - ably = AblyRealtime(key='not_a.real:key', auto_connect=False, vcdiff_decoder=decoder) + ably = create_realtime_client(key='not_a.real:key', auto_connect=False, vcdiff_decoder=decoder) channel = ably.channels.get('delta') received = [] # subscribe() would also wait for the channel to attach, which this client cannot do diff --git a/test/ably/realtime/eventemitter_test.py b/test/ably/realtime/eventemitter_test.py index 71db2c74..f9913248 100644 --- a/test/ably/realtime/eventemitter_test.py +++ b/test/ably/realtime/eventemitter_test.py @@ -2,7 +2,7 @@ import pytest -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/realtime/presencemap_test.py b/test/ably/realtime/presencemap_test.py index 043baeb0..de6002bf 100644 --- a/test/ably/realtime/presencemap_test.py +++ b/test/ably/realtime/presencemap_test.py @@ -8,8 +8,8 @@ import pytest -from ably.realtime.presencemap import PresenceMap, _is_newer -from ably.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.realtime.presencemap import PresenceMap, _is_newer +from ably.pubsub.types.presence import PresenceAction, PresenceMessage from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/realtime/realtimeannotations_test.py b/test/ably/realtime/realtimeannotations_test.py index 2c5e0658..79521766 100644 --- a/test/ably/realtime/realtimeannotations_test.py +++ b/test/ably/realtime/realtimeannotations_test.py @@ -5,10 +5,10 @@ import pytest -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.channelmode import ChannelMode -from ably.types.channeloptions import ChannelOptions -from ably.types.message import MessageAction +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.message import MessageAction from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, ReusableFuture, assert_waiter @@ -334,7 +334,7 @@ async def on_annotation(annotation): pass # RTAN4e: Should log a warning (not raise), and still register the listener - with caplog.at_level(logging.WARNING, logger='ably.realtime.annotations'): + with caplog.at_level(logging.WARNING, logger='ably.pubsub.realtime.annotations'): await channel.annotations.subscribe(on_annotation) # Verify warning was logged mentioning the missing mode diff --git a/test/ably/realtime/realtimeauth_test.py b/test/ably/realtime/realtimeauth_test.py index a2620489..da073117 100644 --- a/test/ably/realtime/realtimeauth_test.py +++ b/test/ably/realtime/realtimeauth_test.py @@ -5,11 +5,11 @@ import httpx import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.connectionstate import ConnectionEvent -from ably.types.tokendetails import TokenDetails +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.connectionstate import ConnectionEvent +from ably.pubsub.types.tokendetails import TokenDetails from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, random_string diff --git a/test/ably/realtime/realtimechannel_publish_test.py b/test/ably/realtime/realtimechannel_publish_test.py index 9ecf10f9..a5af09f5 100644 --- a/test/ably/realtime/realtimechannel_publish_test.py +++ b/test/ably/realtime/realtimechannel_publish_test.py @@ -2,13 +2,13 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channeloptions import ChannelOptions -from ably.types.message import Message -from ably.util.crypto import CipherParams -from ably.util.exceptions import AblyException, IncompatibleClientIdException +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.message import Message +from ably.pubsub.util.crypto import CipherParams +from ably.pubsub.util.exceptions import AblyException, IncompatibleClientIdException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, WaitableEvent, assert_waiter @@ -328,7 +328,7 @@ async def test_msgserial_resets_on_new_connection_id(self): new_connection_id = 'new_connection_id_12345' # Simulate server sending CONNECTED with new connectionId - from ably.types.connectiondetails import ConnectionDetails + from ably.pubsub.types.connectiondetails import ConnectionDetails new_connection_details = ConnectionDetails( connection_state_ttl=120000, max_idle_interval=15000, @@ -365,7 +365,7 @@ async def test_msgserial_not_reset_on_same_connection_id(self): # Simulate reconnection with SAME connectionId (transport change, not new connection) same_connection_id = connection_manager.connection_id - from ably.types.connectiondetails import ConnectionDetails + from ably.pubsub.types.connectiondetails import ConnectionDetails connection_details = ConnectionDetails( connection_state_ttl=120000, max_idle_interval=15000, @@ -491,7 +491,7 @@ async def check_pending(): assert connection_manager.msg_serial == 1 # Simulate resume with SAME connectionId - from ably.types.connectiondetails import ConnectionDetails + from ably.pubsub.types.connectiondetails import ConnectionDetails connection_details = ConnectionDetails( connection_state_ttl=120000, max_idle_interval=15000, @@ -541,7 +541,7 @@ async def check_pending(): assert connection_manager.msg_serial == 1 # Simulate NEW connection (different connectionId = failed resume) - from ably.types.connectiondetails import ConnectionDetails + from ably.pubsub.types.connectiondetails import ConnectionDetails new_connection_details = ConnectionDetails( connection_state_ttl=120000, max_idle_interval=15000, diff --git a/test/ably/realtime/realtimechannel_test.py b/test/ably/realtime/realtimechannel_test.py index 6d2865f2..477f412a 100644 --- a/test/ably/realtime/realtimechannel_test.py +++ b/test/ably/realtime/realtimechannel_test.py @@ -2,12 +2,12 @@ import pytest -from ably.realtime.channel import ChannelState, RealtimeChannel -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channeloptions import ChannelOptions -from ably.types.message import Message -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.channel import ChannelState, RealtimeChannel +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.message import Message +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, random_string diff --git a/test/ably/realtime/realtimechannel_vcdiff_test.py b/test/ably/realtime/realtimechannel_vcdiff_test.py index 48a484a9..ebc6c7c8 100644 --- a/test/ably/realtime/realtimechannel_vcdiff_test.py +++ b/test/ably/realtime/realtimechannel_vcdiff_test.py @@ -3,10 +3,10 @@ import pytest -from ably import AblyVCDiffDecoder -from ably.realtime.connection import ConnectionState -from ably.types.channeloptions import ChannelOptions -from ably.types.options import VCDiffDecoder +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.server import AblyVCDiffDecoder +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.options import VCDiffDecoder from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, WaitableEvent diff --git a/test/ably/realtime/realtimechannelmutablemessages_test.py b/test/ably/realtime/realtimechannelmutablemessages_test.py index afe8a60f..fa4d8db7 100644 --- a/test/ably/realtime/realtimechannelmutablemessages_test.py +++ b/test/ably/realtime/realtimechannelmutablemessages_test.py @@ -3,9 +3,9 @@ import pytest -from ably import AblyException, CipherParams, MessageAction -from ably.types.message import Message -from ably.types.operations import MessageOperation +from ably.pubsub.server import AblyException, CipherParams, MessageAction +from ably.pubsub.types.message import Message +from ably.pubsub.types.operations import MessageOperation from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, WaitableEvent, assert_waiter diff --git a/test/ably/realtime/realtimeconnection_test.py b/test/ably/realtime/realtimeconnection_test.py index d187a378..e6507e6a 100644 --- a/test/ably/realtime/realtimeconnection_test.py +++ b/test/ably/realtime/realtimeconnection_test.py @@ -11,10 +11,10 @@ # websockets 14 and earlier fallback from websockets.server import serve as ws_serve -from ably.realtime.connection import ConnectionEvent, ConnectionState -from ably.transport.defaults import Defaults -from ably.transport.websockettransport import ProtocolMessageAction -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionEvent, ConnectionState +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase @@ -67,7 +67,7 @@ async def close_active_connection(self): @property def endpoint(self) -> str: - """Host to pass to AblyRealtime (combine with tls=False and port=self.port).""" + """Host to pass to DefaultPubSubRealtimeClient (combine with tls=False and port=self.port).""" return "127.0.0.1" async def __aenter__(self): diff --git a/test/ably/realtime/realtimeinit_test.py b/test/ably/realtime/realtimeinit_test.py index 4009d046..1bc5b7ba 100644 --- a/test/ably/realtime/realtimeinit_test.py +++ b/test/ably/realtime/realtimeinit_test.py @@ -2,9 +2,9 @@ import pytest -from ably import Auth -from ably.realtime.connection import ConnectionState -from ably.util.exceptions import AblyAuthException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.server import Auth +from ably.pubsub.util.exceptions import AblyAuthException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/realtime/realtimepresence_test.py b/test/ably/realtime/realtimepresence_test.py index 06067f14..63da265d 100644 --- a/test/ably/realtime/realtimepresence_test.py +++ b/test/ably/realtime/realtimepresence_test.py @@ -9,10 +9,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.types.channelstate import ChannelState -from ably.types.presence import PresenceAction -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.presence import PresenceAction +from ably.pubsub.util.exceptions import AblyException from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase diff --git a/test/ably/realtime/realtimeresume_test.py b/test/ably/realtime/realtimeresume_test.py index bfe77efa..4f3a89e2 100644 --- a/test/ably/realtime/realtimeresume_test.py +++ b/test/ably/realtime/realtimeresume_test.py @@ -2,9 +2,9 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.ably.testapp import TestApp from test.ably.utils import BaseAsyncTestCase, random_string diff --git a/test/ably/testapp.py b/test/ably/testapp.py index 744fd2c7..30a0b6d9 100644 --- a/test/ably/testapp.py +++ b/test/ably/testapp.py @@ -2,12 +2,15 @@ import logging import os -from ably.realtime.realtime import AblyRealtime -from ably.rest.rest import AblyRest -from ably.transport.defaults import Defaults -from ably.types.capability import Capability -from ably.types.options import Options -from ably.util.exceptions import AblyException +# unasync rewrites `from ably.pubsub.server import ...` to the sync package, which +# is HTTP-only. Reaching the realtime factory through the module keeps the +# generated sync copy of this helper importable; it has no realtime tests. +import ably.pubsub.server +from ably.pubsub.server import create_http_client +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.types.capability import Capability +from ably.pubsub.types.options import Options +from ably.pubsub.util.exceptions import AblyException log = logging.getLogger(__name__) @@ -20,10 +23,13 @@ port = 80 tls_port = 443 -ably = AblyRest(token='not_a_real_token', - port=port, tls_port=tls_port, tls=tls, - endpoint=endpoint, - use_binary_protocol=False) +# Not named `ably`: that would shadow the `ably` package imported above, and +# `ably.pubsub.server.create_realtime_client` below would resolve against this +# client instead of the module. +app_setup_client = create_http_client(token='not_a_real_token', + port=port, tls_port=tls_port, tls=tls, + endpoint=endpoint, + use_binary_protocol=False) class TestApp: @@ -32,7 +38,7 @@ class TestApp: @staticmethod async def get_test_vars(): if not TestApp.__test_vars: - r = await ably.http.post("/apps", body=app_spec_local, skip_auth=True) + r = await app_setup_client.http.post("/apps", body=app_spec_local, skip_auth=True) AblyException.raise_for_response(r) app_spec = r.json() @@ -65,7 +71,7 @@ async def get_ably_rest(**kw): test_vars = await TestApp.get_test_vars() options = TestApp.get_options(test_vars, **kw) options.update(kw) - return AblyRest(**options) + return create_http_client(**options) @staticmethod async def get_ably_realtime(**kw): @@ -76,7 +82,7 @@ async def get_ably_realtime(**kw): # for reaching CONNECTED, so a short one keeps a single failed attempt from using # up the whole budget. Tests that assert on retry timing set their own value. options.setdefault('disconnected_retry_timeout', 1000) - return AblyRealtime(**options) + return ably.pubsub.server.create_realtime_client(**options) @staticmethod def get_options(test_vars, **kwargs): diff --git a/test/ably/utils.py b/test/ably/utils.py index a72fdd5c..6715ced7 100644 --- a/test/ably/utils.py +++ b/test/ably/utils.py @@ -11,7 +11,7 @@ import respx from httpx import Response -from ably.http.http import Http +from ably.pubsub.request.http import Http class BaseTestCase: diff --git a/test/unit/annotation_test.py b/test/unit/annotation_test.py index 2cfb0bef..674f14c1 100644 --- a/test/unit/annotation_test.py +++ b/test/unit/annotation_test.py @@ -15,11 +15,11 @@ import pytest -from ably.rest.annotations import construct_validate_annotation, serial_from_msg_or_serial -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.message import Message -from ably.util.crypto import generate_random_key, get_cipher -from ably.util.exceptions import AblyException +from ably.pubsub.http.annotations import construct_validate_annotation, serial_from_msg_or_serial +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.message import Message +from ably.pubsub.util.crypto import generate_random_key, get_cipher +from ably.pubsub.util.exceptions import AblyException # --- RSAN1a3: type validation --- diff --git a/test/unit/clock_test.py b/test/unit/clock_test.py index f8814e3b..b02a5fb6 100644 --- a/test/unit/clock_test.py +++ b/test/unit/clock_test.py @@ -1,10 +1,10 @@ import httpx import pytest -from ably import AblyRest -from ably.types.testoptions import TestOptions -from ably.util.clock import Clock -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_http_client +from ably.pubsub.types.testoptions import TestOptions +from ably.pubsub.util.clock import Clock +from ably.pubsub.util.exceptions import AblyException # A plausible wall-clock reading for a fake to start from, so that a time taken # against it looks like a real one. @@ -56,12 +56,12 @@ async def handle_async_request(self, request): def test_auth_timestamps_from_the_injected_clock(): - ably = AblyRest(key='name:secret', _test_options=TestOptions(clock=FakeClock(1_500_000_000_000))) + ably = create_http_client(key='name:secret', _test_options=TestOptions(clock=FakeClock(1_500_000_000_000))) assert ably.auth._timestamp() == 1_500_000_000_000 def test_a_client_without_test_options_reads_the_real_clock(): - ably = AblyRest(token='foo') + ably = create_http_client(token='foo') assert isinstance(ably.auth._Auth__clock, Clock) assert isinstance(ably.http._Http__clock, Clock) @@ -70,8 +70,8 @@ async def test_the_cached_fallback_host_expires_on_the_clock(): # RSC15f: the cache lasts fallback_retry_timeout milliseconds clock = FakeClock() transport = RecordingTransport() - ably = AblyRest(token='foo', fallback_retry_timeout=2000, - _test_options=TestOptions(http_transport=transport, clock=clock)) + ably = create_http_client(token='foo', fallback_retry_timeout=2000, + _test_options=TestOptions(http_transport=transport, clock=clock)) primary = ably.options.get_host() transport.refusing = (primary,) @@ -96,7 +96,7 @@ async def test_every_host_is_tried_while_the_retry_budget_lasts(): # A second of clock time per reading, against the default 15 second budget clock = FakeClock(step_ms=1000) transport = RecordingTransport() - ably = AblyRest(token='foo', _test_options=TestOptions(http_transport=transport, clock=clock)) + ably = create_http_client(token='foo', _test_options=TestOptions(http_transport=transport, clock=clock)) hosts = ably.http.get_hosts() transport.refusing = tuple(hosts) @@ -112,8 +112,8 @@ async def test_retrying_stops_once_the_retry_budget_is_spent(): # RSC15l: http_max_retry_duration is seconds where the clock is milliseconds clock = FakeClock(step_ms=1000) transport = RecordingTransport() - ably = AblyRest(token='foo', http_max_retry_duration=0.5, - _test_options=TestOptions(http_transport=transport, clock=clock)) + ably = create_http_client(token='foo', http_max_retry_duration=0.5, + _test_options=TestOptions(http_transport=transport, clock=clock)) transport.refusing = tuple(ably.http.get_hosts()) with pytest.raises(AblyException): diff --git a/test/unit/http_test.py b/test/unit/http_test.py index 6418dc00..ebae07b2 100644 --- a/test/unit/http_test.py +++ b/test/unit/http_test.py @@ -1,13 +1,13 @@ import httpx import pytest -from ably import AblyRest -from ably.types.testoptions import TestOptions -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_http_client +from ably.pubsub.types.testoptions import TestOptions +from ably.pubsub.util.exceptions import AblyException def test_http_get_rest_hosts_works_when_fallback_realtime_host_is_set(): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") ably.options.fallback_host = ably.options.get_hosts()[0] # Should not raise TypeError hosts = ably.http.get_hosts() @@ -16,7 +16,7 @@ def test_http_get_rest_hosts_works_when_fallback_realtime_host_is_set(): def test_http_get_rest_hosts_works_when_fallback_realtime_host_is_not_set(): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") ably.options.fallback_host = None # Should not raise TypeError hosts = ably.http.get_hosts() @@ -36,7 +36,7 @@ async def handle_async_request(self, request): async def test_http_sends_requests_through_an_injected_transport(): transport = RecordingTransport(lambda request: httpx.Response(200, json=[1500000000000])) - ably = AblyRest(token="foo", _test_options=TestOptions(http_transport=transport)) + ably = create_http_client(token="foo", _test_options=TestOptions(http_transport=transport)) server_time = await ably.time() @@ -52,7 +52,7 @@ def refuse(request): raise httpx.ConnectError("connection refused", request=request) transport = RecordingTransport(refuse) - ably = AblyRest(token="foo", _test_options=TestOptions(http_transport=transport)) + ably = create_http_client(token="foo", _test_options=TestOptions(http_transport=transport)) with pytest.raises(AblyException): await ably.time() @@ -63,7 +63,7 @@ def refuse(request): def test_http_uses_a_network_transport_without_test_options(): - ably = AblyRest(token="foo") + ably = create_http_client(token="foo") assert isinstance(ably.http._Http__client._transport, httpx.AsyncHTTPTransport) @@ -74,8 +74,8 @@ def respond(request): return httpx.Response(200, json=[1500000000000]) transport = RecordingTransport(respond) - ably = AblyRest(auth_url='https://auth.example.com/token', - _test_options=TestOptions(http_transport=transport)) + ably = create_http_client(auth_url='https://auth.example.com/token', + _test_options=TestOptions(http_transport=transport)) await ably.auth.authorize() diff --git a/test/unit/message_test.py b/test/unit/message_test.py index 917af30c..f49e9bf9 100644 --- a/test/unit/message_test.py +++ b/test/unit/message_test.py @@ -1,4 +1,4 @@ -import ably.types.message +import ably.pubsub.types.message # TM2a, TM2c, TM2f @@ -14,7 +14,7 @@ def test_update_inner_message_fields_tm2(): } ] } - ably.types.message.Message.update_inner_message_fields(proto_msg) + ably.pubsub.types.message.Message.update_inner_message_fields(proto_msg) messages: list[dict] = proto_msg.get('messages') msg_index = 0 for msg in messages: @@ -37,7 +37,7 @@ def test_update_inner_message_fields_for_presence_msg_tm2(): } ] } - ably.types.message.Message.update_inner_message_fields(proto_msg) + ably.pubsub.types.message.Message.update_inner_message_fields(proto_msg) presence_messages: list[dict] = proto_msg.get('presence') msg_index = 0 for presence_msg in presence_messages: @@ -49,7 +49,7 @@ def test_update_inner_message_fields_for_presence_msg_tm2(): # RSL6b def test_invalid_base64_data_is_delivered_with_remaining_encoding(): - message = ably.types.message.Message.from_encoded({ + message = ably.pubsub.types.message.Message.from_encoded({ 'name': 'event', 'data': 'encrypted-data-here', 'encoding': 'custom-encryption/base64', diff --git a/test/unit/mutable_message_test.py b/test/unit/mutable_message_test.py index 8ce603b9..e30ba443 100644 --- a/test/unit/mutable_message_test.py +++ b/test/unit/mutable_message_test.py @@ -1,5 +1,5 @@ -from ably import MessageAction, MessageOperation, MessageVersion, UpdateDeleteResult -from ably.types.message import Message +from ably.pubsub.server import MessageAction, MessageOperation, MessageVersion, UpdateDeleteResult +from ably.pubsub.types.message import Message def test_message_version_none_values_filtered(): @@ -100,7 +100,7 @@ def test_message_version_serialization(): def test_message_extras_preserved_in_as_dict(): """Test that extras are included when a Message with extras is serialized. - Regression test: _send_update() in both RestChannel and RealtimeChannel + Regression test: _send_update() in both HttpChannel and RealtimeChannel constructed a new Message without copying extras or annotations from the user-supplied message, violating RSL15b/RTL32b which require "whatever fields were in the user-supplied Message" to be sent. @@ -137,7 +137,7 @@ def test_message_extras_none_excluded_from_as_dict(): # RSL15b, RTL32b, TM2u def test_message_annotations_preserved_in_as_dict(): """Test that annotations are included when a Message with annotations is serialized.""" - from ably.types.message import MessageAnnotations + from ably.pubsub.types.message import MessageAnnotations annotations = MessageAnnotations(summary={'reaction:distinct.v1': {'thumbsup': 5}}) message = Message( name='test', diff --git a/test/unit/options_test.py b/test/unit/options_test.py index d3ba6129..84cc963b 100644 --- a/test/unit/options_test.py +++ b/test/unit/options_test.py @@ -1,7 +1,7 @@ import pytest -from ably.types.options import Options -from ably.util.exceptions import AblyException +from ably.pubsub.types.options import Options +from ably.pubsub.util.exceptions import AblyException # REC1b1: endpoint is incompatible with deprecated options diff --git a/test/unit/pubsub_server_test.py b/test/unit/pubsub_server_test.py new file mode 100644 index 00000000..a06d2582 --- /dev/null +++ b/test/unit/pubsub_server_test.py @@ -0,0 +1,163 @@ +import inspect +import typing + +import pytest + +import ably.pubsub.server as server +import ably.pubsub.server.sync as server_sync +from ably.pubsub.http.http import DefaultPubSubHttpClient +from ably.pubsub.prototypes import PubSubHttpClient, PubSubRealtimeClient +from ably.pubsub.realtime.realtime import DefaultPubSubRealtimeClient +from ably.pubsub.server import create_http_client, create_realtime_client +from ably.pubsub.sync.http.http import DefaultPubSubHttpClientSync +from ably.pubsub.sync.prototypes import PubSubHttpClient as PubSubHttpClientSync + + +def protocol_members(protocol): + """The members `protocol` declares. + + Python 3.12 records these on the class. Earlier versions only have typing's + own helper, which is the function that builds that attribute, so the two + agree. Reading `vars(protocol)` instead would sweep in `__init__`, + `__abstractmethods__`, `__parameters__` and the rest of the Protocol + machinery, and report them as members the client fails to implement. + """ + members = getattr(protocol, '__protocol_attrs__', None) + if members is None: + members = typing._get_protocol_attrs(protocol) + return set(members) + + +def implementation_classes(protocol, cls): + """The classes in `cls`'s MRO that implement, rather than declare, the prototype. + + The clients subclass their prototype, so every prototype member is reachable + on them whether or not they implement it — an unimplemented one resolves to + the prototype's `...` stub and silently returns None. Dropping the + prototype's own MRO leaves only the classes that can genuinely define a + member. + """ + return [k for k in cls.__mro__ if k not in protocol.__mro__] + + +def declared_member(classes, name): + """The attribute `name` as the first class in `classes` defines it, if any.""" + for k in classes: + if name in k.__dict__: + return k.__dict__[name] + return None + + +def parameter_shape(member): + """A member's callable shape: whether it awaits, and its parameters. + + Annotations are left out deliberately — the prototype spells its types as + strings under `from __future__ import annotations` while the clients + evaluate theirs, and return types are a type checker's business. Parameter + names, kinds and which ones are required are what callers depend on. + """ + fn = member.fget if isinstance(member, property) else member + parameters = [ + (p.name, p.kind, p.default is inspect.Parameter.empty) + for p in inspect.signature(fn).parameters.values() + ] + return inspect.iscoroutinefunction(fn), parameters + + +class TestFactories: + def test_http_factory_returns_a_client(self): + client = create_http_client(token='foo') + assert isinstance(client, DefaultPubSubHttpClient) + + async def test_realtime_factory_returns_a_client(self): + client = create_realtime_client(key='foo:bar', auto_connect=False) + assert isinstance(client, DefaultPubSubRealtimeClient) + await client.close() + + def test_sync_http_factory_returns_a_client(self): + assert isinstance(server_sync.create_http_client(token='foo'), DefaultPubSubHttpClientSync) + + def test_factory_passes_options_through(self): + client = create_http_client(token='foo', client_id='me') + assert client.client_id == 'me' + + +class TestConstructorsAreInternal: + def test_rest_constructor_is_rejected(self): + with pytest.raises(TypeError) as excinfo: + DefaultPubSubHttpClient(token='foo') + assert 'ably.pubsub.server.create_http_client' in str(excinfo.value) + + def test_realtime_constructor_is_rejected(self): + with pytest.raises(TypeError) as excinfo: + DefaultPubSubRealtimeClient(key='foo:bar', auto_connect=False) + assert 'ably.pubsub.server.create_realtime_client' in str(excinfo.value) + + def test_sync_rest_constructor_is_rejected(self): + with pytest.raises(TypeError) as excinfo: + DefaultPubSubHttpClientSync(token='foo') + assert 'ably.pubsub.server.sync.create_http_client' in str(excinfo.value) + + def test_permission_does_not_leak_past_the_factory(self): + create_http_client(token='foo') + with pytest.raises(TypeError): + DefaultPubSubHttpClient(token='foo') + + @pytest.mark.parametrize('name', ['DefaultPubSubHttpClient', 'DefaultPubSubRealtimeClient']) + def test_client_classes_are_not_exported(self, name): + assert not hasattr(server, name) + assert name not in server.__all__ + + def test_sync_client_class_is_not_exported(self): + assert not hasattr(server_sync, 'DefaultPubSubHttpClientSync') + assert 'DefaultPubSubHttpClientSync' not in server_sync.__all__ + + +# Every prototype paired with the client declared to implement it, the +# synchronous flavour included — unasync generates both sides of that pair, +# so the pairing is worth checking rather than assuming. +PROTOTYPE_IMPLEMENTATIONS = [ + (PubSubHttpClient, DefaultPubSubHttpClient), + (PubSubRealtimeClient, DefaultPubSubRealtimeClient), + (PubSubHttpClientSync, DefaultPubSubHttpClientSync), +] + + +class TestPrototypes: + @pytest.mark.parametrize('protocol,cls', PROTOTYPE_IMPLEMENTATIONS) + def test_client_is_declared_to_satisfy_its_prototype(self, protocol, cls): + assert protocol in cls.__mro__ + + @pytest.mark.parametrize('protocol,cls', PROTOTYPE_IMPLEMENTATIONS) + def test_client_implements_every_prototype_member(self, protocol, cls): + owners = implementation_classes(protocol, cls) + missing = sorted( + member for member in protocol_members(protocol) + if declared_member(owners, member) is None + ) + assert missing == [] + + @pytest.mark.parametrize('protocol,cls', PROTOTYPE_IMPLEMENTATIONS) + def test_client_members_match_the_prototype_signature(self, protocol, cls): + owners = implementation_classes(protocol, cls) + mismatched = {} + for member in sorted(protocol_members(protocol)): + expected = parameter_shape(declared_member(protocol.__mro__, member)) + actual = parameter_shape(declared_member(owners, member)) + if expected != actual: + mismatched[member] = {'prototype': expected, 'client': actual} + assert mismatched == {} + + @pytest.mark.parametrize('name', ['PubSubHttpClient', 'PubSubRealtimeClient']) + def test_prototypes_are_exported(self, name): + assert name in server.__all__ + + def test_sync_prototype_is_exported(self): + assert 'PubSubHttpClient' in server_sync.__all__ + + +class TestPublicTypes: + # update_message() takes a Message, so callers need a public way to build one + def test_message_is_exported(self): + assert 'Message' in server.__all__ + assert 'Message' in server_sync.__all__ diff --git a/test/unit/websockettransport_test.py b/test/unit/websockettransport_test.py index d45c3ca8..3cf1fffb 100644 --- a/test/unit/websockettransport_test.py +++ b/test/unit/websockettransport_test.py @@ -1,7 +1,7 @@ from unittest.mock import MagicMock, patch -from ably.transport.websockettransport import WebSocketTransport -from ably.types.options import Options +from ably.pubsub.transport.websockettransport import WebSocketTransport +from ably.pubsub.types.options import Options def _connect_url(host='example.com', **option_kwargs): @@ -9,7 +9,7 @@ def _connect_url(host='example.com', **option_kwargs): connection_manager.options = Options(auth_token='foo', **option_kwargs) transport = WebSocketTransport(connection_manager, host, {'format': 'json'}) with patch.object(transport, 'ws_connect', MagicMock()) as mock_ws_connect: - with patch('ably.transport.websockettransport.asyncio.create_task') as mock_create_task: + with patch('ably.pubsub.transport.websockettransport.asyncio.create_task') as mock_create_task: mock_create_task.return_value = MagicMock() transport.connect() return mock_ws_connect.call_args[0][0] diff --git a/test/uts/README.md b/test/uts/README.md index acab71e5..8690d290 100644 --- a/test/uts/README.md +++ b/test/uts/README.md @@ -40,7 +40,7 @@ mock_http = MockHttpClient( on_connection_attempt=lambda conn: conn.respond_with_success(), on_request=lambda req: req.respond_with(200, {'result': 'ok'}), ) -ably = AblyRest(key=key, _test_options=TestOptions(http_transport=mock_http.as_transport())) +ably = create_http_client(key=key, _test_options=TestOptions(http_transport=mock_http.as_transport())) ``` A realtime client takes its websocket mock the same way, through @@ -51,9 +51,9 @@ A realtime client takes its websocket mock the same way, through mock_ws = MockWebSocket( on_connection_attempt=lambda conn: conn.respond_with_success(CONNECTED_MESSAGE), ) -ably = AblyRealtime(key=key, auto_connect=False, - _test_options=TestOptions(websocket_connect=mock_ws.as_connect(), - clock=FakeClock())) +ably = create_realtime_client(key=key, auto_connect=False, + _test_options=TestOptions(websocket_connect=mock_ws.as_connect(), + clock=FakeClock())) ``` `rest_client(mock_http, ...)` and `realtime_client(mock_ws, ...)` in diff --git a/test/uts/deviations.md b/test/uts/deviations.md index bf08ee74..498f0ad3 100644 --- a/test/uts/deviations.md +++ b/test/uts/deviations.md @@ -75,7 +75,7 @@ from the specification text, so that correcting the specification is all it take it pass, and it is marked `@spec_error` — a skip gated on `RUN_DEVIATIONS`, the same gate `@deviation` uses, with a reason naming the specification rather than the SDK. The suite stays green, a real regression still shows, and the failure is one environment variable -away. Each is filed upstream, in the issues named below. Ten tests are gated +away. Each is filed upstream, in the issues named below. Eleven tests are gated this way: | Test | Spec error | @@ -90,6 +90,7 @@ this way: | `test_rsp4_history_pagination` | RSP4 - wire action 4 asserted to be LEAVE | | `test_tp3_presence_to_json` | TP3 - an outgoing action asserted as the string `"enter"` | | `test_tp3_null_attributes_omitted` | TP3 - the same outgoing string assertion | +| `test_rsc7d_ably_agent_header_format` | RSC7d - a library-name pattern that rejects hyphens | Where instead only a specification's *fixture*, *setup* or *label* is at fault, the assertion it carries still stands. Those tests keep the corrected fixture (or the @@ -139,7 +140,7 @@ RSA4c3 contradiction, since that issue is what decides it. `#547` to `#550` are the `uts/rest/integration` faults and `#551` to `#554` the `uts/realtime/integration` ones. Each of them is of the second kind — a fixture, a setup step or a header label — so the derived test keeps the corrected fixture and passes, and -none of them is among the ten gated above. +none of them is among the eleven gated above. Not every entry has an issue of its own: the URL-safe base64 alphabet is recorded below and not filed, because ably-python's own encoding settles the tests either way. Line @@ -153,6 +154,15 @@ references in these entries are against `ably/specification@d9a04ca`. returns a one-element array, which is what `time.md` itself uses and what RSC16 describes. Any SDK that indexes the array raises. +### RSC7d's Agent pattern rejects a hyphenated library name + +`rest_client.md`'s `RSC7d/ably-agent-header-format-0` asserts that the `Ably-Agent` +header matches `ably-[a-z]+/[0-9]+\.[0-9]+\.[0-9]+`. RSC7d1 asks only for a series of +`key[/value]` entries that include the library's name and version, and puts no limit on +the characters a key may hold. ably-python reports itself as `ably-pubsub-python/`, +which RSC7d1 allows and the pattern rejects. The test is gated with `@spec_error`. The +fault is not filed upstream yet. + ### Spec points are mislabelled across four specs The assertions are sound; the points they are filed under are not. diff --git a/test/uts/helpers/client.py b/test/uts/helpers/client.py index 57db6e6a..061a6f6e 100644 --- a/test/uts/helpers/client.py +++ b/test/uts/helpers/client.py @@ -4,8 +4,8 @@ import inspect import logging -from ably import AblyRealtime, AblyRest -from ably.types.testoptions import TestOptions +from ably.pubsub.server import create_http_client, create_realtime_client +from ably.pubsub.types.testoptions import TestOptions from test.uts.helpers.clock import settle from test.uts.helpers.sandbox import SANDBOX_ENDPOINT @@ -41,7 +41,7 @@ def rest_client(mock_http, clock=None, **kwargs): if not any(option in kwargs for option in CREDENTIAL_OPTIONS): kwargs['key'] = DEFAULT_KEY test_options = TestOptions(http_transport=mock_http.as_transport(), clock=clock) - client = AblyRest(_test_options=test_options, **kwargs) + client = create_http_client(_test_options=test_options, **kwargs) __open_clients.append(client) return client @@ -63,7 +63,7 @@ def realtime_client(mock_websocket=None, mock_http=None, clock=None, **kwargs): kwargs['key'] = DEFAULT_KEY kwargs.setdefault('auto_connect', False) kwargs.setdefault('fallback_hosts', []) - client = AblyRealtime(_test_options=TestOptions( + client = create_realtime_client(_test_options=TestOptions( http_transport=mock_http.as_transport() if mock_http is not None else None, websocket_connect=mock_websocket.as_connect() if mock_websocket is not None else None, clock=clock, @@ -92,7 +92,7 @@ def sandbox_rest_client(key=None, **kwargs): kwargs['key'] = key kwargs.setdefault('endpoint', SANDBOX_ENDPOINT) kwargs.setdefault('use_binary_protocol', False) - client = AblyRest(**kwargs) + client = create_http_client(**kwargs) __open_clients.append(client) return client @@ -112,7 +112,7 @@ def sandbox_realtime_client(key=None, **kwargs): kwargs['key'] = key kwargs.setdefault('endpoint', SANDBOX_ENDPOINT) kwargs.setdefault('use_binary_protocol', False) - client = AblyRealtime(**kwargs) + client = create_realtime_client(**kwargs) __open_clients.append(client) return client @@ -274,7 +274,7 @@ async def connected_client(mock_websocket, **kwargs): Most channel specifications open this way, since a channel cannot attach until the connection carrying it is up. """ - from ably.realtime.connection import ConnectionState + from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.mock_websocket import CONNECTED_MESSAGE if mock_websocket.on_connection_attempt is None: @@ -293,7 +293,7 @@ async def drop_transport(client, mock_websocket): specification expects to find it rather than reconnecting behind the assertions. Returns the connection states recorded along the way. """ - from ably.realtime.connection import ConnectionState + from ably.pubsub.realtime.connection import ConnectionState states = [] @@ -316,7 +316,7 @@ async def reconnect_transport(client, mock_websocket, connected_message=None): Waiting on the connection state alone would be satisfied by the CONNECTED the client already holds, so this counts a fresh arrival. """ - from ably.realtime.connection import ConnectionState + from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.mock_websocket import CONNECTED_MESSAGE message = CONNECTED_MESSAGE if connected_message is None else connected_message diff --git a/test/uts/helpers/clock.py b/test/uts/helpers/clock.py index 6d818715..c2a07720 100644 --- a/test/uts/helpers/clock.py +++ b/test/uts/helpers/clock.py @@ -61,7 +61,7 @@ def __repr__(self): class FakeClock: """Records timers against a notional time which only `advance` moves. - `FakeClock` has the shape of `ably.util.clock.Clock`, so it can be passed + `FakeClock` has the shape of `ably.pubsub.util.clock.Clock`, so it can be passed straight to `TestOptions(clock=...)`. Nothing fires until a test asks for it, and every reading the client takes moves only when `advance` does. """ diff --git a/test/uts/helpers/mock_http_test.py b/test/uts/helpers/mock_http_test.py index d860a7d0..e48d65a8 100644 --- a/test/uts/helpers/mock_http_test.py +++ b/test/uts/helpers/mock_http_test.py @@ -6,9 +6,9 @@ import msgpack import pytest -from ably import AblyRest -from ably.types.testoptions import TestOptions -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_http_client +from ably.pubsub.types.testoptions import TestOptions +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.mock_http import MockHttpClient KEY = 'a.b:c' @@ -20,7 +20,7 @@ def rest_client(mock, **kwargs): """A REST client whose HTTP calls are served by `mock`.""" - return AblyRest(key=KEY, _test_options=TestOptions(http_transport=mock.as_transport()), **kwargs) + return create_http_client(key=KEY, _test_options=TestOptions(http_transport=mock.as_transport()), **kwargs) async def raw_get(ably, path='/time'): @@ -175,7 +175,8 @@ def connect(connection): mock = MockHttpClient(on_connection_attempt=connect, on_request=lambda request: request.respond_with(200, [SERVER_TIME])) - ably = AblyRest(token='foo', tls=False, _test_options=TestOptions(http_transport=mock.as_transport())) + ably = create_http_client(token='foo', tls=False, + _test_options=TestOptions(http_transport=mock.as_transport())) await ably.time() diff --git a/test/uts/helpers/mock_websocket.py b/test/uts/helpers/mock_websocket.py index bbf0075a..fc89bb93 100644 --- a/test/uts/helpers/mock_websocket.py +++ b/test/uts/helpers/mock_websocket.py @@ -27,7 +27,7 @@ import msgpack from websockets.exceptions import WebSocketException -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.mock_http import RecordedUrl MSGPACK_PROTOCOL = 'application/x-msgpack' diff --git a/test/uts/helpers/mock_websocket_test.py b/test/uts/helpers/mock_websocket_test.py index dc2866f2..6ecb74a3 100644 --- a/test/uts/helpers/mock_websocket_test.py +++ b/test/uts/helpers/mock_websocket_test.py @@ -12,8 +12,8 @@ import msgpack import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock from test.uts.helpers.mock_http import MockHttpClient @@ -189,7 +189,7 @@ async def test_a_pending_connection_carries_the_headers_the_client_sent(): headers = mock.connection_attempts[0].headers assert 'ably-agent' in headers - assert 'ably-python' in headers['Ably-Agent'] + assert 'ably-pubsub-python' in headers['Ably-Agent'] async def test_the_protocol_follows_use_binary_protocol(): diff --git a/test/uts/helpers/presence.py b/test/uts/helpers/presence.py index 4d493bdc..cec057a9 100644 --- a/test/uts/helpers/presence.py +++ b/test/uts/helpers/presence.py @@ -8,10 +8,10 @@ from types import SimpleNamespace -from ably.realtime.presence import RealtimePresence -from ably.realtime.presencemap import PresenceMap -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.realtime.presence import RealtimePresence +from ably.pubsub.realtime.presencemap import PresenceMap +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.presence import PresenceAction, PresenceMessage #: The lowercase action names `RealtimePresence` emits its events under, which come #: from `PresenceAction._action_name` and exist only once `presence.py` is imported. diff --git a/test/uts/helpers/sandbox.py b/test/uts/helpers/sandbox.py index 0fb89fec..a29236c3 100644 --- a/test/uts/helpers/sandbox.py +++ b/test/uts/helpers/sandbox.py @@ -4,7 +4,7 @@ POST the canonical app setup body to the sandbox, take the keys out of the response, and DELETE the app when the tests are done. -Provisioning goes over plain `httpx` rather than through `AblyRest`. It is +Provisioning goes over plain `httpx` rather than through `DefaultPubSubHttpClient`. It is infrastructure, and a client that cannot form a request would otherwise look like a broken fixture rather than a failing test. @@ -80,7 +80,7 @@ def fixture_cipher_params(): key and the IV base64-encoded, which is not what `CipherParams` wants: it takes them as raw bytes and derives the key length from the key. """ - from ably.util.crypto import CipherParams + from ably.pubsub.util.crypto import CipherParams return CipherParams( algorithm=CIPHER_FIXTURE['algorithm'], diff --git a/test/uts/realtime/integration/auth/token_renewal_test.py b/test/uts/realtime/integration/auth/token_renewal_test.py index a1c4b25f..e8e4407c 100644 --- a/test/uts/realtime/integration/auth/token_renewal_test.py +++ b/test/uts/realtime/integration/auth/token_renewal_test.py @@ -13,7 +13,7 @@ introduce a value no assertion consumes. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt diff --git a/test/uts/realtime/integration/auth/token_request_test.py b/test/uts/realtime/integration/auth/token_request_test.py index 285c8e26..9729b8c0 100644 --- a/test/uts/realtime/integration/auth/token_request_test.py +++ b/test/uts/realtime/integration/auth/token_request_test.py @@ -15,7 +15,7 @@ case, so `TokenParams(clientId: x)` is `{'client_id': x}`. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, sandbox_rest_client from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/auth_test.py b/test/uts/realtime/integration/auth_test.py index 7f82d260..3bb60ae2 100644 --- a/test/uts/realtime/integration/auth_test.py +++ b/test/uts/realtime/integration/auth_test.py @@ -15,7 +15,7 @@ registered beforehand by the time the call completes. The reauth test needs no settle. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt, random_id diff --git a/test/uts/realtime/integration/channel_history_test.py b/test/uts/realtime/integration/channel_history_test.py index d49eb95a..ae16404e 100644 --- a/test/uts/realtime/integration/channel_history_test.py +++ b/test/uts/realtime/integration/channel_history_test.py @@ -19,7 +19,7 @@ specification writes raises `ValueError`. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/channels/channel_attach_test.py b/test/uts/realtime/integration/channels/channel_attach_test.py index 68f31243..707ffb5d 100644 --- a/test/uts/realtime/integration/channels/channel_attach_test.py +++ b/test/uts/realtime/integration/channels/channel_attach_test.py @@ -6,7 +6,7 @@ `use_binary_protocol`; `sandbox_realtime_client` already defaults to it. `RealtimeChannel.publish()` takes its arguments positionally. The keyword form the -specification writes, which `RestChannel.publish()` does accept, raises +specification writes, which `HttpChannel.publish()` does accept, raises `ValueError: publish() expects either (name, data) or a message object or array of messages` before anything reaches the server. @@ -18,9 +18,9 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, sandbox_realtime_client from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/channels/channel_publish_test.py b/test/uts/realtime/integration/channels/channel_publish_test.py index 53b9800a..d106a243 100644 --- a/test/uts/realtime/integration/channels/channel_publish_test.py +++ b/test/uts/realtime/integration/channels/channel_publish_test.py @@ -6,7 +6,7 @@ per protocol and passes `use_binary_protocol` to both clients it builds. `RealtimeChannel.publish()` takes its arguments positionally; the keyword form the -specification writes, which `RestChannel.publish()` does accept, raises `ValueError`. +specification writes, which `HttpChannel.publish()` does accept, raises `ValueError`. A binary payload arrives as a `bytearray` rather than `bytes` under either protocol, so the type assertion reads both. `bytearray` compares equal to the `bytes` that was @@ -22,8 +22,8 @@ spells out for the same wait. """ -from ably.realtime.connection import ConnectionState -from ably.types.message import Message +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.message import Message from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/channels/channel_subscribe_test.py b/test/uts/realtime/integration/channels/channel_subscribe_test.py index c8850930..99a1022e 100644 --- a/test/uts/realtime/integration/channels/channel_subscribe_test.py +++ b/test/uts/realtime/integration/channels/channel_subscribe_test.py @@ -20,7 +20,7 @@ spells out for the same wait. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/connection/connection_failures_test.py b/test/uts/realtime/integration/connection/connection_failures_test.py index 3e31a24e..fa4cf3b9 100644 --- a/test/uts/realtime/integration/connection/connection_failures_test.py +++ b/test/uts/realtime/integration/connection/connection_failures_test.py @@ -18,7 +18,7 @@ 1008. It is noise from teardown, not a failure of either test. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client # The wait the specification gives each connect attempt. diff --git a/test/uts/realtime/integration/connection_lifecycle_test.py b/test/uts/realtime/integration/connection_lifecycle_test.py index ffa0c126..d58a66ba 100644 --- a/test/uts/realtime/integration/connection_lifecycle_test.py +++ b/test/uts/realtime/integration/connection_lifecycle_test.py @@ -16,7 +16,7 @@ import re -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client # The waits the specification's `Integration Test Notes` give each leg: auth plus transport diff --git a/test/uts/realtime/integration/delta_decoding_test.py b/test/uts/realtime/integration/delta_decoding_test.py index 6538d84b..f6c8ef48 100644 --- a/test/uts/realtime/integration/delta_decoding_test.py +++ b/test/uts/realtime/integration/delta_decoding_test.py @@ -7,7 +7,7 @@ vcdiff delta, and the SDK decodes it against the payload it stored for the previous one. The specification's `plugins: { vcdiff: decoder }` is the `vcdiff_decoder` client option -here, and its `VCDiffDecoder` interface is `ably.types.options.VCDiffDecoder` — +here, and its `VCDiffDecoder` interface is `ably.pubsub.types.options.VCDiffDecoder` — `decode(delta, base) -> bytes`, the VD2a argument order. `AblyVCDiffDecoder` is the real implementation, backed by the `vcdiff-decoder` library; `CountingDecoder` wraps it so a test can assert how many deltas the server actually sent. @@ -20,11 +20,11 @@ import os -from ably import AblyVCDiffDecoder -from ably.realtime.connection import ConnectionState -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState -from ably.types.options import VCDiffDecoder +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.server import AblyVCDiffDecoder +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.options import VCDiffDecoder from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/integration/mutable_messages_test.py b/test/uts/realtime/integration/mutable_messages_test.py index 8d19691a..481f50da 100644 --- a/test/uts/realtime/integration/mutable_messages_test.py +++ b/test/uts/realtime/integration/mutable_messages_test.py @@ -18,15 +18,15 @@ consistent; the two polls below are the specification's `poll_until_success` around them. """ -from ably.http.paginatedresult import PaginatedResult -from ably.realtime.connection import ConnectionState -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.channelmode import ChannelMode -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState -from ably.types.message import Message, MessageAction -from ably.types.operations import MessageOperation, UpdateDeleteResult -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.message import Message, MessageAction +from ably.pubsub.types.operations import MessageOperation, UpdateDeleteResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/integration/presence/presence_sync_test.py b/test/uts/realtime/integration/presence/presence_sync_test.py index 6a78481d..20a46ed6 100644 --- a/test/uts/realtime/integration/presence/presence_sync_test.py +++ b/test/uts/realtime/integration/presence/presence_sync_test.py @@ -17,8 +17,8 @@ behalf of others is built with `client_id='*'`. """ -from ably.realtime.connection import ConnectionState -from ably.types.presence import PresenceAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.client import await_connection_state, sandbox_realtime_client from test.uts.helpers.sandbox import random_id diff --git a/test/uts/realtime/integration/presence_lifecycle_test.py b/test/uts/realtime/integration/presence_lifecycle_test.py index a6a74603..0a8a7c70 100644 --- a/test/uts/realtime/integration/presence_lifecycle_test.py +++ b/test/uts/realtime/integration/presence_lifecycle_test.py @@ -22,8 +22,8 @@ import asyncio -from ably.realtime.connection import ConnectionState -from ably.types.presence import PresenceAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.client import ( await_connection_state, sandbox_realtime_client, diff --git a/test/uts/realtime/integration/proxy/auth_reauth_test.py b/test/uts/realtime/integration/proxy/auth_reauth_test.py index 847f66f1..8e47f75e 100644 --- a/test/uts/realtime/integration/proxy/auth_reauth_test.py +++ b/test/uts/realtime/integration/proxy/auth_reauth_test.py @@ -22,7 +22,7 @@ an `auth` attribute left the client, and that the connection was never disturbed. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt diff --git a/test/uts/realtime/integration/proxy/channel_faults_test.py b/test/uts/realtime/integration/proxy/channel_faults_test.py index dbcdd378..09ae9907 100644 --- a/test/uts/realtime/integration/proxy/channel_faults_test.py +++ b/test/uts/realtime/integration/proxy/channel_faults_test.py @@ -28,9 +28,9 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/integration/proxy/connection_open_failures_test.py b/test/uts/realtime/integration/proxy/connection_open_failures_test.py index 58206871..9b7de332 100644 --- a/test/uts/realtime/integration/proxy/connection_open_failures_test.py +++ b/test/uts/realtime/integration/proxy/connection_open_failures_test.py @@ -26,8 +26,8 @@ callback, because renewing a token is what it is about. """ -from ably import AblyRest -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.server import create_http_client from test.uts.helpers.client import await_connection_state, sandbox_realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_websocket import contains_in_order @@ -99,7 +99,7 @@ def token_auth_callback(api_key): again; the token arrives over a connection the proxy never sees. """ async def auth_callback(params): - inner_rest = AblyRest(key=api_key, endpoint=SANDBOX_ENDPOINT) + inner_rest = create_http_client(key=api_key, endpoint=SANDBOX_ENDPOINT) try: return await inner_rest.auth.request_token() finally: diff --git a/test/uts/realtime/integration/proxy/connection_resume_test.py b/test/uts/realtime/integration/proxy/connection_resume_test.py index 22e042b9..6f93dff8 100644 --- a/test/uts/realtime/integration/proxy/connection_resume_test.py +++ b/test/uts/realtime/integration/proxy/connection_resume_test.py @@ -39,8 +39,8 @@ import asyncio import json -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import sandbox_realtime_client, sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.deviations import deviation from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt, random_id diff --git a/test/uts/realtime/integration/proxy/heartbeat_test.py b/test/uts/realtime/integration/proxy/heartbeat_test.py index b9602be6..c90a1b90 100644 --- a/test/uts/realtime/integration/proxy/heartbeat_test.py +++ b/test/uts/realtime/integration/proxy/heartbeat_test.py @@ -27,7 +27,7 @@ waiting for it, so the reconnection settles instead of cycling. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, sandbox_realtime_client, wall_clock_poll_until from test.uts.helpers.mock_websocket import contains_in_order from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt diff --git a/test/uts/realtime/integration/proxy/presence_reentry_test.py b/test/uts/realtime/integration/proxy/presence_reentry_test.py index 9e20a4c0..674ae6e9 100644 --- a/test/uts/realtime/integration/proxy/presence_reentry_test.py +++ b/test/uts/realtime/integration/proxy/presence_reentry_test.py @@ -26,8 +26,8 @@ from datetime import datetime -from ably.realtime.connection import ConnectionState -from ably.types.channelstate import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.channelstate import ChannelState from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/integration/proxy/rest_faults_test.py b/test/uts/realtime/integration/proxy/rest_faults_test.py index fa8d2c1a..dcbf0f38 100644 --- a/test/uts/realtime/integration/proxy/rest_faults_test.py +++ b/test/uts/realtime/integration/proxy/rest_faults_test.py @@ -25,10 +25,10 @@ import pytest -from ably import AblyRest -from ably.realtime.connection import ConnectionState -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.server import create_http_client +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, @@ -67,7 +67,7 @@ def token_auth_callback(api_key, invocations=None): async def auth_callback(params): if invocations is not None: invocations.append(params) - inner_rest = AblyRest(key=api_key, endpoint=SANDBOX_ENDPOINT) + inner_rest = create_http_client(key=api_key, endpoint=SANDBOX_ENDPOINT) try: return await inner_rest.auth.request_token() finally: diff --git a/test/uts/realtime/unit/auth/auth_callback_errors_test.py b/test/uts/realtime/unit/auth/auth_callback_errors_test.py index 6b941e92..7f056457 100644 --- a/test/uts/realtime/unit/auth/auth_callback_errors_test.py +++ b/test/uts/realtime/unit/auth/auth_callback_errors_test.py @@ -8,10 +8,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, poll_until, realtime_client, rest_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/auth/connection_auth_test.py b/test/uts/realtime/unit/auth/connection_auth_test.py index 8beaa42f..a1c32b77 100644 --- a/test/uts/realtime/unit/auth/connection_auth_test.py +++ b/test/uts/realtime/unit/auth/connection_auth_test.py @@ -5,10 +5,10 @@ import time -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/auth/realtime_authorize_test.py b/test/uts/realtime/unit/auth/realtime_authorize_test.py index 568611c0..aa9589bf 100644 --- a/test/uts/realtime/unit/auth/realtime_authorize_test.py +++ b/test/uts/realtime/unit/auth/realtime_authorize_test.py @@ -8,11 +8,11 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionEvent, ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionEvent, ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/auth/token_expiry_non_renewable_test.py b/test/uts/realtime/unit/auth/token_expiry_non_renewable_test.py index 2c36c07e..c8dbaab6 100644 --- a/test/uts/realtime/unit/auth/token_expiry_non_renewable_test.py +++ b/test/uts/realtime/unit/auth/token_expiry_non_renewable_test.py @@ -5,7 +5,7 @@ import logging -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_websocket import ERROR_MESSAGE, MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/channels/channel_additional_attached_test.py b/test/uts/realtime/unit/channels/channel_additional_attached_test.py index eb9b64e9..3cd559f7 100644 --- a/test/uts/realtime/unit/channels/channel_additional_attached_test.py +++ b/test/uts/realtime/unit/channels/channel_additional_attached_test.py @@ -3,10 +3,10 @@ Spec points: RTL12 """ -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/channels/channel_annotations_test.py b/test/uts/realtime/unit/channels/channel_annotations_test.py index 61fa13f5..b70a1e4a 100644 --- a/test/uts/realtime/unit/channels/channel_annotations_test.py +++ b/test/uts/realtime/unit/channels/channel_annotations_test.py @@ -21,13 +21,13 @@ import pytest -from ably.realtime.annotations import RealtimeAnnotations -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.annotations import RealtimeAnnotations +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import MockWebSocket, attached_message, connected_message @@ -378,7 +378,7 @@ async def test_rtan4e_subscribe_warns_no_mode(caplog): mock_ws = annotating_mock(channel_name, flags=Flag.PUBLISH) channel = await attached_channel(mock_ws, channel_name) - with caplog.at_level('WARNING', logger='ably.realtime.annotations'): + with caplog.at_level('WARNING', logger='ably.pubsub.realtime.annotations'): await channel.annotations.subscribe(lambda annotation: None) warnings = [record.getMessage() for record in caplog.records if record.levelname == 'WARNING'] @@ -402,7 +402,7 @@ async def test_rtan4e1_no_warn_unattached(caplog): assert channel.state == ChannelState.INITIALIZED - with caplog.at_level('WARNING', logger='ably.realtime.annotations'): + with caplog.at_level('WARNING', logger='ably.pubsub.realtime.annotations'): subscribing = asyncio.ensure_future(channel.annotations.subscribe(lambda annotation: None)) await poll_until(lambda: channel.state == ChannelState.ATTACHING, description='the implicit attach is in flight') diff --git a/test/uts/realtime/unit/channels/channel_attach_test.py b/test/uts/realtime/unit/channels/channel_attach_test.py index 1be3771c..8f6b05d7 100644 --- a/test/uts/realtime/unit/channels/channel_attach_test.py +++ b/test/uts/realtime/unit/channels/channel_attach_test.py @@ -8,13 +8,13 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelmode import ChannelMode -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_attributes_test.py b/test/uts/realtime/unit/channels/channel_attributes_test.py index 78d09e00..aeeeaff2 100644 --- a/test/uts/realtime/unit/channels/channel_attributes_test.py +++ b/test/uts/realtime/unit/channels/channel_attributes_test.py @@ -7,10 +7,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_connection_state_test.py b/test/uts/realtime/unit/channels/channel_connection_state_test.py index b2b65f50..d1b2a947 100644 --- a/test/uts/realtime/unit/channels/channel_connection_state_test.py +++ b/test/uts/realtime/unit/channels/channel_connection_state_test.py @@ -7,10 +7,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_delta_decoding_test.py b/test/uts/realtime/unit/channels/channel_delta_decoding_test.py index fa71c466..c3a3262e 100644 --- a/test/uts/realtime/unit/channels/channel_delta_decoding_test.py +++ b/test/uts/realtime/unit/channels/channel_delta_decoding_test.py @@ -22,10 +22,10 @@ import base64 import uuid -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.options import VCDiffDecoder +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.options import VCDiffDecoder from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/channels/channel_detach_test.py b/test/uts/realtime/unit/channels/channel_detach_test.py index 90c8b397..e5a2a17d 100644 --- a/test/uts/realtime/unit/channels/channel_detach_test.py +++ b/test/uts/realtime/unit/channels/channel_detach_test.py @@ -7,10 +7,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_error_test.py b/test/uts/realtime/unit/channels/channel_error_test.py index b135966b..268eca60 100644 --- a/test/uts/realtime/unit/channels/channel_error_test.py +++ b/test/uts/realtime/unit/channels/channel_error_test.py @@ -7,10 +7,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_get_message_test.py b/test/uts/realtime/unit/channels/channel_get_message_test.py index 7a2cb2ab..4019ea2a 100644 --- a/test/uts/realtime/unit/channels/channel_get_message_test.py +++ b/test/uts/realtime/unit/channels/channel_get_message_test.py @@ -4,13 +4,13 @@ The specification points at uts/rest/unit/channel/get_message.md (RSL11) rather than listing steps of its own, so its single Test ID becomes one test driving an -`AblyRealtime` over the HTTP mock and asserting the core observable of the derived +`DefaultPubSubRealtimeClient` over the HTTP mock and asserting the core observable of the derived REST suite: the endpoint the call reaches and the `Message` it returns. """ import uuid -from ably.types.message import Message +from ably.pubsub.types.message import Message from test.uts.helpers.client import realtime_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/realtime/unit/channels/channel_history_test.py b/test/uts/realtime/unit/channels/channel_history_test.py index 8f5647b2..3675e239 100644 --- a/test/uts/realtime/unit/channels/channel_history_test.py +++ b/test/uts/realtime/unit/channels/channel_history_test.py @@ -3,7 +3,7 @@ Spec points: RTL10, RTL10a, RTL10b, RTL10c RTL10a points at uts/rest/unit/channel/history.md (RSL2) rather than listing steps of -its own, so its Test ID becomes one test driving an `AblyRealtime` over the HTTP mock +its own, so its Test ID becomes one test driving an `DefaultPubSubRealtimeClient` over the HTTP mock and asserting the core observable of the derived REST suite. The two RTL10b tests cover `untilAttach`, which ably-python does not implement; see @@ -14,11 +14,11 @@ import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient @@ -59,9 +59,9 @@ def on_message_from_client(msg): # UTS: realtime/unit/RTL10a/supports-rest-params-0 async def test_rtl10a_supports_rest_params(): # The specification directs uts/rest/unit/channel/history.md (RSL2) at a realtime - # channel in place of a REST one. `AblyRealtime` subclasses `AblyRest`, so the same - # HTTP mock serves it, and this mirrors that suite's `RSL2a/returns-paginated-result-0` - # and `RSL2b/query-parameters-0`. + # channel in place of a REST one. `DefaultPubSubRealtimeClient` subclasses + # `DefaultPubSubHttpClient`, so the same HTTP mock serves it, and this mirrors that + # suite's `RSL2a/returns-paginated-result-0` and `RSL2b/query-parameters-0`. channel_name = f'test-RTL10a-{random_id()}' captured_requests = [] @@ -86,7 +86,7 @@ async def test_rtl10a_supports_rest_params(): assert captured_requests[0].url.path == f'/channels/{channel_name}/messages' - # RTL10a: the parameters `RestChannel#history` takes + # RTL10a: the parameters `HttpChannel#history` takes await channel.history(direction='forwards', limit=50, start=1000, end=2000) query_params = captured_requests[1].url.query_params diff --git a/test/uts/realtime/unit/channels/channel_message_versions_test.py b/test/uts/realtime/unit/channels/channel_message_versions_test.py index ffac77fd..c8ea4a5f 100644 --- a/test/uts/realtime/unit/channels/channel_message_versions_test.py +++ b/test/uts/realtime/unit/channels/channel_message_versions_test.py @@ -4,14 +4,14 @@ The specification points at uts/rest/unit/channel/message_versions.md (RSL14) rather than listing steps of its own, so its single Test ID becomes one test driving an -`AblyRealtime` over the HTTP mock and asserting the core observable of the derived +`DefaultPubSubRealtimeClient` over the HTTP mock and asserting the core observable of the derived REST suite: the endpoint the call reaches and the paginated versions it returns. """ import uuid -from ably.http.paginatedresult import PaginatedResult -from ably.types.message import Message, MessageAction +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.message import Message, MessageAction from test.uts.helpers.client import realtime_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/realtime/unit/channels/channel_options_test.py b/test/uts/realtime/unit/channels/channel_options_test.py index 43284aa8..3da5dd78 100644 --- a/test/uts/realtime/unit/channels/channel_options_test.py +++ b/test/uts/realtime/unit/channels/channel_options_test.py @@ -15,13 +15,13 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelmode import ChannelMode -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState -from ably.util.crypto import get_default_params -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelmode import ChannelMode +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.crypto import get_default_params +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, @@ -106,7 +106,7 @@ async def test_tb2d_options_with_modes(): async def test_tb3_with_cipher_key(): # DEVIATION: TB3's `withCipherKey` constructor is absent from `ChannelOptions` # (`ably/types/channeloptions.py`), which takes a `CipherParams` and offers no factory - # that builds one from a key. `ably.util.crypto.get_default_params({'key': key})` is the + # that builds one from a key. `ably.pubsub.util.crypto.get_default_params({'key': key})` is the # nearest equivalent, and it is not on ChannelOptions. options = ChannelOptions.with_cipher_key(CIPHER_KEY) @@ -261,7 +261,7 @@ async def test_rts5a_creates_derived_channel(): # `DeriveOptions`. `Channels.__getattr__` (`ably/rest/channel.py:408`) answers any # unknown attribute with a channel of that name, so the call raises # `TypeError: 'RealtimeChannel' object is not callable` rather than AttributeError. - from ably import DeriveOptions + from ably.pubsub.server import DeriveOptions base_channel_name = 'test-RTS5a' client = realtime_client() @@ -278,7 +278,7 @@ async def test_rts5a_creates_derived_channel(): @deviation async def test_rts5a1_filter_base64_encoded(): # DEVIATION: derived channels are absent; see the RTS5a test. - from ably import DeriveOptions + from ably.pubsub.server import DeriveOptions base_channel_name = 'test-RTS5a1' client = realtime_client() @@ -296,7 +296,7 @@ async def test_rts5a1_filter_base64_encoded(): @deviation async def test_rts5a2_derived_with_params(): # DEVIATION: derived channels are absent; see the RTS5a test. - from ably import DeriveOptions + from ably.pubsub.server import DeriveOptions base_channel_name = 'test-RTS5a2' client = realtime_client() @@ -323,7 +323,7 @@ async def test_rts5a2_derived_with_params(): async def test_rts5_get_derived_with_options(): # DEVIATION: derived channels are absent; see the RTS5a test. `attachOnSubscribe` is # absent too; see the TB4 test. - from ably import DeriveOptions + from ably.pubsub.server import DeriveOptions base_channel_name = 'test-RTS5' client = realtime_client() @@ -341,7 +341,7 @@ async def test_rts5_get_derived_with_options(): @deviation async def test_do2a_filter_attribute(): # DEVIATION: `DeriveOptions` is absent from the library, so the import fails. - from ably import DeriveOptions + from ably.pubsub.server import DeriveOptions derive_options = DeriveOptions(filter="name == 'event' && data.count > 10") diff --git a/test/uts/realtime/unit/channels/channel_properties_test.py b/test/uts/realtime/unit/channels/channel_properties_test.py index dfa0bcff..212f6f79 100644 --- a/test/uts/realtime/unit/channels/channel_properties_test.py +++ b/test/uts/realtime/unit/channels/channel_properties_test.py @@ -10,10 +10,10 @@ import asyncio -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_publish_pending_test.py b/test/uts/realtime/unit/channels/channel_publish_pending_test.py index aa5e6e20..337edfd2 100644 --- a/test/uts/realtime/unit/channels/channel_publish_pending_test.py +++ b/test/uts/realtime/unit/channels/channel_publish_pending_test.py @@ -15,11 +15,11 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.operations import PublishResult -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.operations import PublishResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( STATE_TIMEOUT, await_channel_state, diff --git a/test/uts/realtime/unit/channels/channel_publish_test.py b/test/uts/realtime/unit/channels/channel_publish_test.py index 73d4ac41..bd531b82 100644 --- a/test/uts/realtime/unit/channels/channel_publish_test.py +++ b/test/uts/realtime/unit/channels/channel_publish_test.py @@ -15,7 +15,7 @@ as a task. `RealtimeChannel.publish()` takes its arguments positionally; the keyword form the -specifications write, which `RestChannel.publish()` does accept, raises `ValueError` here. +specifications write, which `HttpChannel.publish()` does accept, raises `ValueError` here. """ import asyncio @@ -25,12 +25,12 @@ import msgpack import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.message import Message -from ably.types.operations import PublishResult -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.message import Message +from ably.pubsub.types.operations import PublishResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/channels/channel_server_initiated_detach_test.py b/test/uts/realtime/unit/channels/channel_server_initiated_detach_test.py index f531f587..fc681546 100644 --- a/test/uts/realtime/unit/channels/channel_server_initiated_detach_test.py +++ b/test/uts/realtime/unit/channels/channel_server_initiated_detach_test.py @@ -7,10 +7,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/channels/channel_state_events_test.py b/test/uts/realtime/unit/channels/channel_state_events_test.py index 8d800808..eb5ad4ac 100644 --- a/test/uts/realtime/unit/channels/channel_state_events_test.py +++ b/test/uts/realtime/unit/channels/channel_state_events_test.py @@ -7,11 +7,11 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState, ChannelStateChange -from ably.types.flags import Flag -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState, ChannelStateChange +from ably.pubsub.types.flags import Flag +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/channels/channel_subscribe_test.py b/test/uts/realtime/unit/channels/channel_subscribe_test.py index 354ff5d3..4361a72e 100644 --- a/test/uts/realtime/unit/channels/channel_subscribe_test.py +++ b/test/uts/realtime/unit/channels/channel_subscribe_test.py @@ -8,10 +8,10 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, @@ -46,7 +46,7 @@ def message_protocol_message(channel_name, messages, **fields): def message_filter(**criteria): """The specification's `MessageFilter` (MFI1), built from `criteria`.""" - from ably.types.messagefilter import MessageFilter + from ably.pubsub.types.messagefilter import MessageFilter return MessageFilter(**criteria) @@ -285,7 +285,7 @@ def accept_attach(msg): # UTS: realtime/unit/RTL7h/no-attach-on-subscribe-0 @deviation async def test_rtl7h_no_attach_on_subscribe(): - from ably.types.channeloptions import ChannelOptions + from ably.pubsub.types.channeloptions import ChannelOptions channel_name = 'test-RTL7h' attach_messages = [] diff --git a/test/uts/realtime/unit/channels/channel_update_delete_message_test.py b/test/uts/realtime/unit/channels/channel_update_delete_message_test.py index 6af97755..80867ef0 100644 --- a/test/uts/realtime/unit/channels/channel_update_delete_message_test.py +++ b/test/uts/realtime/unit/channels/channel_update_delete_message_test.py @@ -12,11 +12,11 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.message import Message, MessageAction -from ably.types.operations import MessageOperation, UpdateDeleteResult -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.message import Message, MessageAction +from ably.pubsub.types.operations import MessageOperation, UpdateDeleteResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.mock_websocket import MockWebSocket, attached_message, connected_message diff --git a/test/uts/realtime/unit/channels/channel_when_state_test.py b/test/uts/realtime/unit/channels/channel_when_state_test.py index 76bd651e..252901f2 100644 --- a/test/uts/realtime/unit/channels/channel_when_state_test.py +++ b/test/uts/realtime/unit/channels/channel_when_state_test.py @@ -5,9 +5,9 @@ import asyncio -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/channels/channels_collection_test.py b/test/uts/realtime/unit/channels/channels_collection_test.py index 3498ee12..e455a584 100644 --- a/test/uts/realtime/unit/channels/channels_collection_test.py +++ b/test/uts/realtime/unit/channels/channels_collection_test.py @@ -11,11 +11,11 @@ import asyncio -from ably.realtime.channel import Channels as RealtimeChannels -from ably.realtime.channel import RealtimeChannel -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState +from ably.pubsub.realtime.channel import Channels as RealtimeChannels +from ably.pubsub.realtime.channel import RealtimeChannel +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_websocket import ( diff --git a/test/uts/realtime/unit/channels/message_field_population_test.py b/test/uts/realtime/unit/channels/message_field_population_test.py index 8efcd7df..b358442e 100644 --- a/test/uts/realtime/unit/channels/message_field_population_test.py +++ b/test/uts/realtime/unit/channels/message_field_population_test.py @@ -3,8 +3,8 @@ Spec points: TM2a, TM2c, TM2f """ -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import ( await_connection_state, poll_until, diff --git a/test/uts/realtime/unit/client/realtime_client_test.py b/test/uts/realtime/unit/client/realtime_client_test.py index 2e765a4c..3c548191 100644 --- a/test/uts/realtime/unit/client/realtime_client_test.py +++ b/test/uts/realtime/unit/client/realtime_client_test.py @@ -8,13 +8,13 @@ import pytest -from ably.realtime.channel import Channels, RealtimeChannel -from ably.realtime.connection import Connection, ConnectionState -from ably.rest.auth import Auth -from ably.rest.push import Push, PushAdmin -from ably.transport.websockettransport import ProtocolMessageAction -from ably.util.exceptions import AblyException -from ably.util.helper import get_random_id +from ably.pubsub.http.auth import Auth +from ably.pubsub.http.push import Push, PushAdmin +from ably.pubsub.realtime.channel import Channels, RealtimeChannel +from ably.pubsub.realtime.connection import Connection, ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.util.exceptions import AblyException +from ably.pubsub.util.helper import get_random_id from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation @@ -144,7 +144,7 @@ async def test_rtc17_client_id_attribute(): assert client.client_id == 'explicit-client-id' # DEVIATION: the spec asserts `client.clientId == client.auth.clientId`. - # `AblyRealtime.client_id` reads the client options, while `Auth.client_id` is + # `DefaultPubSubRealtimeClient.client_id` reads the client options, while `Auth.client_id` is # held at None for a realtime client until the server confirms one in a CONNECTED # message, so the two disagree before the connection is established. assert client.auth.client_id is None diff --git a/test/uts/realtime/unit/client/realtime_request_test.py b/test/uts/realtime/unit/client/realtime_request_test.py index 3a0fe289..858e9df4 100644 --- a/test/uts/realtime/unit/client/realtime_request_test.py +++ b/test/uts/realtime/unit/client/realtime_request_test.py @@ -17,7 +17,7 @@ # UTS: realtime/unit/RTC9/request-proxies-rest-0 async def test_rtc9_request_proxies_rest(): # The specification directs uts/rest/unit/request.md (RSC19) at a realtime client - # in place of a REST one. `AblyRealtime` subclasses `AblyRest`, so the same HTTP + # in place of a REST one. `DefaultPubSubRealtimeClient` subclasses `DefaultPubSubHttpClient`, so the same HTTP # mock serves it, and this mirrors that suite's `RSC19f/supports-http-methods-0` # and `RSC19d/response-items-decoded-5`. captured_requests = [] diff --git a/test/uts/realtime/unit/client/realtime_stats_test.py b/test/uts/realtime/unit/client/realtime_stats_test.py index ad9d8a44..a4fd40be 100644 --- a/test/uts/realtime/unit/client/realtime_stats_test.py +++ b/test/uts/realtime/unit/client/realtime_stats_test.py @@ -3,7 +3,7 @@ Spec points: RTC5, RTC5a, RTC5b """ -from ably.http.paginatedresult import PaginatedResult +from ably.pubsub.request.paginatedresult import PaginatedResult from test.uts.helpers.client import realtime_client from test.uts.helpers.mock_http import MockHttpClient @@ -30,7 +30,7 @@ # UTS: realtime/unit/RTC5/stats-proxies-rest-0 async def test_rtc5_stats_proxies_rest(): # The specification directs uts/rest/unit/stats.md (RSC6) at a realtime client - # in place of a REST one. `AblyRealtime` subclasses `AblyRest`, so the same HTTP + # in place of a REST one. `DefaultPubSubRealtimeClient` subclasses `DefaultPubSubHttpClient`, so the same HTTP # mock serves it, and this mirrors that suite's `RSC6a/returns-paginated-stats-0`. captured_requests = [] diff --git a/test/uts/realtime/unit/client/realtime_time_test.py b/test/uts/realtime/unit/client/realtime_time_test.py index 64c1fa8e..162336ed 100644 --- a/test/uts/realtime/unit/client/realtime_time_test.py +++ b/test/uts/realtime/unit/client/realtime_time_test.py @@ -12,7 +12,7 @@ # UTS: realtime/unit/RTC6/time-proxies-rest-0 async def test_rtc6_time_proxies_rest(): # The specification directs uts/rest/unit/time.md (RSC16) at a realtime client - # in place of a REST one. `AblyRealtime` subclasses `AblyRest`, so the same + # in place of a REST one. `DefaultPubSubRealtimeClient` subclasses `DefaultPubSubHttpClient`, so the same # HTTP mock serves it, and this mirrors that suite's `RSC16/returns-server-time-0`. captured_requests = [] diff --git a/test/uts/realtime/unit/client/realtime_timeouts_test.py b/test/uts/realtime/unit/client/realtime_timeouts_test.py index 6b0d3eba..3a9c21d9 100644 --- a/test/uts/realtime/unit/client/realtime_timeouts_test.py +++ b/test/uts/realtime/unit/client/realtime_timeouts_test.py @@ -7,11 +7,11 @@ import pytest -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.util.exceptions import AblyException -from ably.util.helper import get_random_id +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.util.exceptions import AblyException +from ably.pubsub.util.helper import get_random_id from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/realtime/unit/connection/auto_connect_test.py b/test/uts/realtime/unit/connection/auto_connect_test.py index 29e8e12f..f77c0c39 100644 --- a/test/uts/realtime/unit/connection/auto_connect_test.py +++ b/test/uts/realtime/unit/connection/auto_connect_test.py @@ -3,7 +3,7 @@ Spec points: RTN3 """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/connection/backoff_jitter_test.py b/test/uts/realtime/unit/connection/backoff_jitter_test.py index 35b865b5..eee5a880 100644 --- a/test/uts/realtime/unit/connection/backoff_jitter_test.py +++ b/test/uts/realtime/unit/connection/backoff_jitter_test.py @@ -12,9 +12,9 @@ exact rather than sampled. """ -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/connection/connection_failures_test.py b/test/uts/realtime/unit/connection/connection_failures_test.py index b2ea02c7..ffbcd2c8 100644 --- a/test/uts/realtime/unit/connection/connection_failures_test.py +++ b/test/uts/realtime/unit/connection/connection_failures_test.py @@ -5,8 +5,8 @@ import time -from ably.realtime.connection import ConnectionState -from ably.types.tokendetails import TokenDetails +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.tokendetails import TokenDetails from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/connection/connection_id_key_test.py b/test/uts/realtime/unit/connection/connection_id_key_test.py index adfcd9aa..32f1ecbc 100644 --- a/test/uts/realtime/unit/connection/connection_id_key_test.py +++ b/test/uts/realtime/unit/connection/connection_id_key_test.py @@ -3,8 +3,8 @@ Spec points: RTN8, RTN8a, RTN8b, RTN8d, RTN9, RTN9a, RTN9b, RTN9d """ -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/connection/connection_open_failures_test.py b/test/uts/realtime/unit/connection/connection_open_failures_test.py index af1e7393..0224e5b1 100644 --- a/test/uts/realtime/unit/connection/connection_open_failures_test.py +++ b/test/uts/realtime/unit/connection/connection_open_failures_test.py @@ -5,8 +5,8 @@ import time -from ably.realtime.connection import ConnectionState -from ably.types.tokendetails import TokenDetails +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.tokendetails import TokenDetails from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/realtime/unit/connection/connection_ping_test.py b/test/uts/realtime/unit/connection/connection_ping_test.py index 1c3aaad7..dc3abd62 100644 --- a/test/uts/realtime/unit/connection/connection_ping_test.py +++ b/test/uts/realtime/unit/connection/connection_ping_test.py @@ -12,8 +12,8 @@ import asyncio -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import ( await_connection_state, poll_until, diff --git a/test/uts/realtime/unit/connection/connection_recovery_test.py b/test/uts/realtime/unit/connection/connection_recovery_test.py index abc92660..9684513c 100644 --- a/test/uts/realtime/unit/connection/connection_recovery_test.py +++ b/test/uts/realtime/unit/connection/connection_recovery_test.py @@ -13,9 +13,9 @@ import asyncio import json -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_channel_state, await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/connection/error_reason_test.py b/test/uts/realtime/unit/connection/error_reason_test.py index 2ad7bca8..a0a3622e 100644 --- a/test/uts/realtime/unit/connection/error_reason_test.py +++ b/test/uts/realtime/unit/connection/error_reason_test.py @@ -3,8 +3,8 @@ Spec points: RTN25 """ -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import FakeClock, settle from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/connection/fallback_hosts_test.py b/test/uts/realtime/unit/connection/fallback_hosts_test.py index bcad6f43..69294ba8 100644 --- a/test/uts/realtime/unit/connection/fallback_hosts_test.py +++ b/test/uts/realtime/unit/connection/fallback_hosts_test.py @@ -24,9 +24,9 @@ import httpx -from ably.realtime import connectionmanager -from ably.realtime.connection import ConnectionState -from ably.transport.defaults import Defaults +from ably.pubsub.realtime import connectionmanager +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.defaults import Defaults from test.uts.helpers.client import await_connection_state, poll_until, realtime_client from test.uts.helpers.mock_http import MockHttpClient from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/connection/forwards_compatibility_test.py b/test/uts/realtime/unit/connection/forwards_compatibility_test.py index 78f287b1..fcf0a8db 100644 --- a/test/uts/realtime/unit/connection/forwards_compatibility_test.py +++ b/test/uts/realtime/unit/connection/forwards_compatibility_test.py @@ -8,9 +8,9 @@ fields as written and no separate method is needed. """ -from ably.realtime.channel import ChannelState -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.realtime.channel import ChannelState +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import HEARTBEAT_MESSAGE, MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/connection/heartbeat_test.py b/test/uts/realtime/unit/connection/heartbeat_test.py index 681da166..42b0efd5 100644 --- a/test/uts/realtime/unit/connection/heartbeat_test.py +++ b/test/uts/realtime/unit/connection/heartbeat_test.py @@ -11,7 +11,7 @@ import pytest -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import ( await_connection_state, poll_until, diff --git a/test/uts/realtime/unit/connection/server_initiated_reauth_test.py b/test/uts/realtime/unit/connection/server_initiated_reauth_test.py index 3b0fa5c4..7c24e0f2 100644 --- a/test/uts/realtime/unit/connection/server_initiated_reauth_test.py +++ b/test/uts/realtime/unit/connection/server_initiated_reauth_test.py @@ -5,9 +5,9 @@ import time -from ably.realtime.connection import ConnectionEvent, ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.tokendetails import TokenDetails +from ably.pubsub.realtime.connection import ConnectionEvent, ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.tokendetails import TokenDetails from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/connection/update_events_test.py b/test/uts/realtime/unit/connection/update_events_test.py index 34f51b09..cfe5790f 100644 --- a/test/uts/realtime/unit/connection/update_events_test.py +++ b/test/uts/realtime/unit/connection/update_events_test.py @@ -3,8 +3,8 @@ Spec points: RTN24 """ -from ably.realtime.connection import ConnectionState -from ably.types.connectionstate import ConnectionEvent +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.types.connectionstate import ConnectionEvent from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/connection/when_state_test.py b/test/uts/realtime/unit/connection/when_state_test.py index 63f2f9d0..d41c4fc1 100644 --- a/test/uts/realtime/unit/connection/when_state_test.py +++ b/test/uts/realtime/unit/connection/when_state_test.py @@ -5,7 +5,7 @@ import asyncio -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import STATE_TIMEOUT, await_connection_state, realtime_client from test.uts.helpers.clock import settle from test.uts.helpers.mock_websocket import MockWebSocket, connected_message diff --git a/test/uts/realtime/unit/presence/local_presence_map_test.py b/test/uts/realtime/unit/presence/local_presence_map_test.py index 9e2e5ce2..1048143a 100644 --- a/test/uts/realtime/unit/presence/local_presence_map_test.py +++ b/test/uts/realtime/unit/presence/local_presence_map_test.py @@ -10,8 +10,8 @@ test/uts/deviations.md. """ -from ably.realtime.presencemap import PresenceMap -from ably.types.presence import PresenceAction +from ably.pubsub.realtime.presencemap import PresenceMap +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.deviations import deviation from test.uts.helpers.presence import presence_message, subscribed_presence diff --git a/test/uts/realtime/unit/presence/presence_map_test.py b/test/uts/realtime/unit/presence/presence_map_test.py index 7277096e..8a09145c 100644 --- a/test/uts/realtime/unit/presence/presence_map_test.py +++ b/test/uts/realtime/unit/presence/presence_map_test.py @@ -11,7 +11,7 @@ test/uts/deviations.md. """ -from ably.types.presence import PresenceAction +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.presence import presence_map, presence_message, subscribed_presence diff --git a/test/uts/realtime/unit/presence/presence_sync_test.py b/test/uts/realtime/unit/presence/presence_sync_test.py index 2e291c7d..0d228862 100644 --- a/test/uts/realtime/unit/presence/presence_sync_test.py +++ b/test/uts/realtime/unit/presence/presence_sync_test.py @@ -12,7 +12,7 @@ from datetime import datetime, timezone -from ably.types.presence import PresenceAction +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.deviations import deviation from test.uts.helpers.presence import presence_map, presence_message, subscribed_presence diff --git a/test/uts/realtime/unit/presence/realtime_presence_channel_state_test.py b/test/uts/realtime/unit/presence/realtime_presence_channel_state_test.py index 39341669..15d50b36 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_channel_state_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_channel_state_test.py @@ -19,13 +19,13 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.realtime.presence import RealtimePresence -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.types.presence import PresenceAction -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.realtime.presence import RealtimePresence +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.types.presence import PresenceAction +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, connected_client, diff --git a/test/uts/realtime/unit/presence/realtime_presence_enter_test.py b/test/uts/realtime/unit/presence/realtime_presence_enter_test.py index 01ad53e5..9168d22a 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_enter_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_enter_test.py @@ -19,12 +19,12 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.types.presence import PresenceAction -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.types.presence import PresenceAction +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/presence/realtime_presence_get_test.py b/test/uts/realtime/unit/presence/realtime_presence_get_test.py index 78fc6712..d0afbf7c 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_get_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_get_test.py @@ -19,11 +19,11 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/realtime/unit/presence/realtime_presence_history_test.py b/test/uts/realtime/unit/presence/realtime_presence_history_test.py index c9943c74..59b60675 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_history_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_history_test.py @@ -14,10 +14,10 @@ import uuid -from ably.http.paginatedresult import PaginatedResult -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.presence import PresenceAction, PresenceMessage from test.uts.helpers.client import await_connection_state, realtime_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/realtime/unit/presence/realtime_presence_reentry_test.py b/test/uts/realtime/unit/presence/realtime_presence_reentry_test.py index 702dd77d..0de3607e 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_reentry_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_reentry_test.py @@ -15,10 +15,10 @@ import asyncio import uuid -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.types.presence import PresenceAction +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.client import await_channel_state, connected_client, poll_until from test.uts.helpers.clock import settle from test.uts.helpers.deviations import deviation diff --git a/test/uts/realtime/unit/presence/realtime_presence_subscribe_test.py b/test/uts/realtime/unit/presence/realtime_presence_subscribe_test.py index cec42e4f..66b562d5 100644 --- a/test/uts/realtime/unit/presence/realtime_presence_subscribe_test.py +++ b/test/uts/realtime/unit/presence/realtime_presence_subscribe_test.py @@ -10,12 +10,12 @@ import uuid -from ably.realtime.connection import ConnectionState -from ably.transport.websockettransport import ProtocolMessageAction -from ably.types.channeloptions import ChannelOptions -from ably.types.channelstate import ChannelState -from ably.types.flags import Flag -from ably.types.presence import PresenceAction +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.transport.websockettransport import ProtocolMessageAction +from ably.pubsub.types.channeloptions import ChannelOptions +from ably.pubsub.types.channelstate import ChannelState +from ably.pubsub.types.flags import Flag +from ably.pubsub.types.presence import PresenceAction from test.uts.helpers.client import ( await_channel_state, await_connection_state, diff --git a/test/uts/rest/integration/auth_test.py b/test/uts/rest/integration/auth_test.py index eac29ee2..9d7c253d 100644 --- a/test/uts/rest/integration/auth_test.py +++ b/test/uts/rest/integration/auth_test.py @@ -17,8 +17,8 @@ import pytest -from ably.transport.defaults import Defaults -from ably.util.exceptions import AblyException +from ably.pubsub.transport.defaults import Defaults +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import sandbox_rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.sandbox import extract_key_name, extract_key_secret, generate_jwt, random_id diff --git a/test/uts/rest/integration/batch_presence_test.py b/test/uts/rest/integration/batch_presence_test.py index 2ccb356b..b9d4e7af 100644 --- a/test/uts/rest/integration/batch_presence_test.py +++ b/test/uts/rest/integration/batch_presence_test.py @@ -2,7 +2,7 @@ Spec points: RSC24, BGR2, BGF2 -DEVIATION: ably-python has no batch API. `AblyRest` exposes no `batch_presence`, and +DEVIATION: ably-python has no batch API. `DefaultPubSubHttpClient` exposes no `batch_presence`, and the package defines neither `BatchResult` nor `BatchPresenceSuccessResult` / `BatchPresenceFailureResult`; the word "batch" appears nowhere under `ably/`. Every test here therefore departs from the specification and is gated behind @@ -26,7 +26,7 @@ *Auth*. """ -from ably.realtime.connection import ConnectionState +from ably.pubsub.realtime.connection import ConnectionState from test.uts.helpers.client import ( await_connection_state, sandbox_realtime_client, diff --git a/test/uts/rest/integration/mutable_messages_test.py b/test/uts/rest/integration/mutable_messages_test.py index b632b628..225302af 100644 --- a/test/uts/rest/integration/mutable_messages_test.py +++ b/test/uts/rest/integration/mutable_messages_test.py @@ -3,11 +3,11 @@ Spec points: RSL1n, RSL11, RSL14, RSL15, RSAN1, RSAN2, RSAN3 """ -from ably.http.paginatedresult import PaginatedResult -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.message import Message, MessageAction -from ably.types.operations import MessageOperation, PublishResult, UpdateDeleteResult -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.message import Message, MessageAction +from ably.pubsub.types.operations import MessageOperation, PublishResult, UpdateDeleteResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/rest/integration/pagination_test.py b/test/uts/rest/integration/pagination_test.py index d7cacd6d..984d3044 100644 --- a/test/uts/rest/integration/pagination_test.py +++ b/test/uts/rest/integration/pagination_test.py @@ -3,8 +3,8 @@ Spec points: TG1, TG2, TG3, TG4, TG5 """ -from ably.http.paginatedresult import PaginatedResult -from ably.types.message import Message +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.message import Message from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/rest/integration/presence_test.py b/test/uts/rest/integration/presence_test.py index 7f2a013e..6a3a5ab5 100644 --- a/test/uts/rest/integration/presence_test.py +++ b/test/uts/rest/integration/presence_test.py @@ -5,10 +5,10 @@ import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.realtime.connection import ConnectionState -from ably.types.presence import Presence, PresenceAction, PresenceMessage -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.presence import Presence, PresenceAction, PresenceMessage +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_connection_state, sandbox_realtime_client, @@ -65,7 +65,7 @@ async def test_rsp1_access_presence_from_channel(sandbox, use_binary_protocol): assert presence is not None # NOTE: the spec asserts `presence IS RestPresence`. The class behind - # `channel.presence` in ably-python is `ably.types.presence.Presence`. + # `channel.presence` in ably-python is `ably.pubsub.types.presence.Presence`. assert isinstance(presence, Presence) diff --git a/test/uts/rest/integration/proxy/rest_fallback_test.py b/test/uts/rest/integration/proxy/rest_fallback_test.py index 27eb1727..aed99207 100644 --- a/test/uts/rest/integration/proxy/rest_fallback_test.py +++ b/test/uts/rest/integration/proxy/rest_fallback_test.py @@ -19,8 +19,8 @@ import pytest -from ably import AblyRest -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_http_client +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.deviations import deviation from test.uts.helpers.sandbox import SANDBOX_ENDPOINT, random_id @@ -44,7 +44,7 @@ def token_auth_callback(api_key): it: the token arrives over a connection the proxy never sees. """ async def auth_callback(params): - inner_rest = AblyRest(key=api_key, endpoint=SANDBOX_ENDPOINT) + inner_rest = create_http_client(key=api_key, endpoint=SANDBOX_ENDPOINT) try: return await inner_rest.auth.request_token() finally: diff --git a/test/uts/rest/integration/publish_test.py b/test/uts/rest/integration/publish_test.py index b81ca9dc..30b817bc 100644 --- a/test/uts/rest/integration/publish_test.py +++ b/test/uts/rest/integration/publish_test.py @@ -5,8 +5,8 @@ import pytest -from ably.types.message import Message -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/rest/integration/push_admin_test.py b/test/uts/rest/integration/push_admin_test.py index 5b4a5f45..16720dd3 100644 --- a/test/uts/rest/integration/push_admin_test.py +++ b/test/uts/rest/integration/push_admin_test.py @@ -6,8 +6,8 @@ import pytest -from ably import AblyException, DeviceDetails, PushChannelSubscription -from ably.http.paginatedresult import PaginatedResult +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.server import AblyException, DeviceDetails, PushChannelSubscription from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.sandbox import random_id diff --git a/test/uts/rest/integration/push_channels_test.py b/test/uts/rest/integration/push_channels_test.py index 36f0d551..79b60a65 100644 --- a/test/uts/rest/integration/push_channels_test.py +++ b/test/uts/rest/integration/push_channels_test.py @@ -4,11 +4,11 @@ DEVIATION: ably-python implements neither the PushChannel interface (RSH7, the `push` field on a channel) nor LocalDevice (RSH8). `ably/rest/channel.py` gives a channel no -`push`, `AblyRest` no `device`, and `ably/types/device.py` defines only `DeviceDetails`. +`push`, `DefaultPubSubHttpClient` no `device`, and `ably/types/device.py` defines only `DeviceDetails`. Both tests here therefore depart from the specification and are gated behind RUN_DEVIATIONS, against the same spelling [test/uts/rest/unit/push/push_channels_test.py](../unit/push/push_channels_test.py) -gates on — `ably.types.device.LocalDevice`, `client.device` and +gates on — `ably.pubsub.types.device.LocalDevice`, `client.device` and `channel.push.subscribe_device()` and friends — so that dropping the marker is the only change either tier needs when RSH7 lands. @@ -31,7 +31,7 @@ Errors*. """ -from ably import AblyException, DeviceDetails +from ably.pubsub.server import AblyException, DeviceDetails from test.uts.helpers.client import sandbox_rest_client, wall_clock_poll_until from test.uts.helpers.deviations import deviation from test.uts.helpers.sandbox import random_id @@ -55,7 +55,7 @@ def set_local_device(client, device_id, device_identity_token=PLACEHOLDER_DEVICE The import is deliberately inside the call, so that a file-level import of a name that does not exist does not take the collection of the whole package down with it. """ - from ably.types.device import LocalDevice + from ably.pubsub.types.device import LocalDevice client.device = LocalDevice( id=device_id, diff --git a/test/uts/rest/integration/revoke_tokens_test.py b/test/uts/rest/integration/revoke_tokens_test.py index 7b2adacd..c83a5da1 100644 --- a/test/uts/rest/integration/revoke_tokens_test.py +++ b/test/uts/rest/integration/revoke_tokens_test.py @@ -27,8 +27,8 @@ import pytest -from ably.realtime.connection import ConnectionState -from ably.util.exceptions import AblyException +from ably.pubsub.realtime.connection import ConnectionState +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import ( await_connection_state, next_connection_state, diff --git a/test/uts/rest/integration/time_stats_test.py b/test/uts/rest/integration/time_stats_test.py index 39d6e465..b2d3abb7 100644 --- a/test/uts/rest/integration/time_stats_test.py +++ b/test/uts/rest/integration/time_stats_test.py @@ -8,9 +8,9 @@ import pytest_asyncio -from ably import AblyRest -from ably.http.paginatedresult import PaginatedResult -from ably.types.stats import Stats +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.server import create_http_client +from ably.pubsub.types.stats import Stats from test.uts.helpers.client import sandbox_rest_client from test.uts.helpers.sandbox import SANDBOX_ENDPOINT @@ -48,8 +48,8 @@ async def app_with_stats(sandbox): """ interval = (datetime.now(timezone.utc).replace(tzinfo=None) - STATS_INTERVAL_AGE).replace( second=0, microsecond=0) - client = AblyRest(key=sandbox.key(0).key_str, endpoint=SANDBOX_ENDPOINT, - use_binary_protocol=False) + client = create_http_client(key=sandbox.key(0).key_str, endpoint=SANDBOX_ENDPOINT, + use_binary_protocol=False) try: await client.http.post('/stats', body=[{ 'intervalId': Stats.to_interval_id(interval, 'minute'), diff --git a/test/uts/rest/unit/auth/auth_callback_test.py b/test/uts/rest/unit/auth/auth_callback_test.py index 235abbb0..4511aefe 100644 --- a/test/uts/rest/unit/auth/auth_callback_test.py +++ b/test/uts/rest/unit/auth/auth_callback_test.py @@ -9,10 +9,10 @@ import pytest -from ably import api_version -from ably.types.tokendetails import TokenDetails -from ably.types.tokenrequest import TokenRequest -from ably.util.exceptions import AblyException +from ably.pubsub.server import api_version +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.types.tokenrequest import TokenRequest +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/auth_scheme_test.py b/test/uts/rest/unit/auth/auth_scheme_test.py index 30ed44cc..e69b732a 100644 --- a/test/uts/rest/unit/auth/auth_scheme_test.py +++ b/test/uts/rest/unit/auth/auth_scheme_test.py @@ -8,9 +8,9 @@ import pytest -from ably import api_version -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.server import api_version +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/authorize_test.py b/test/uts/rest/unit/auth/authorize_test.py index a280fc2a..5d1f50d3 100644 --- a/test/uts/rest/unit/auth/authorize_test.py +++ b/test/uts/rest/unit/auth/authorize_test.py @@ -9,9 +9,9 @@ import pytest -from ably.types.authoptions import AuthOptions -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.types.authoptions import AuthOptions +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation, spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/client_id_test.py b/test/uts/rest/unit/auth/client_id_test.py index 5dcc536b..58a2990e 100644 --- a/test/uts/rest/unit/auth/client_id_test.py +++ b/test/uts/rest/unit/auth/client_id_test.py @@ -8,8 +8,8 @@ import pytest -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException, IncompatibleClientIdException +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException, IncompatibleClientIdException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/revoke_tokens_test.py b/test/uts/rest/unit/auth/revoke_tokens_test.py index f8a02085..0adb05f9 100644 --- a/test/uts/rest/unit/auth/revoke_tokens_test.py +++ b/test/uts/rest/unit/auth/revoke_tokens_test.py @@ -17,7 +17,7 @@ import msgpack import pytest -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/token_details_test.py b/test/uts/rest/unit/auth/token_details_test.py index e7e1bc88..d4031743 100644 --- a/test/uts/rest/unit/auth/token_details_test.py +++ b/test/uts/rest/unit/auth/token_details_test.py @@ -7,10 +7,10 @@ import pytest -from ably.types.authoptions import AuthOptions -from ably.types.capability import Capability -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.types.authoptions import AuthOptions +from ably.pubsub.types.capability import Capability +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/auth/token_renewal_test.py b/test/uts/rest/unit/auth/token_renewal_test.py index 6f4f6243..2918d1a1 100644 --- a/test/uts/rest/unit/auth/token_renewal_test.py +++ b/test/uts/rest/unit/auth/token_renewal_test.py @@ -9,8 +9,8 @@ import msgpack import pytest -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/batch_presence_test.py b/test/uts/rest/unit/batch_presence_test.py index 187d61ad..599a3892 100644 --- a/test/uts/rest/unit/batch_presence_test.py +++ b/test/uts/rest/unit/batch_presence_test.py @@ -2,7 +2,7 @@ Spec points: RSC24, BAR2, BGR2, BGF2 -NOTE: ably-python has no batch API. `AblyRest` exposes no `batch_presence`, and the package +NOTE: ably-python has no batch API. `DefaultPubSubHttpClient` exposes no `batch_presence`, and the package defines neither `BatchResult`/`BatchPresenceResponse` nor `BatchPresenceSuccessResult`/ `BatchPresenceFailureResult`; the word "batch" appears nowhere under `ably/`. Every test in this file therefore departs from the specification and is gated behind `RUN_DEVIATIONS`. @@ -20,8 +20,8 @@ import pytest -from ably.types.presence import PresenceAction -from ably.util.exceptions import AblyException +from ably.pubsub.types.presence import PresenceAction +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/batch_publish_test.py b/test/uts/rest/unit/batch_publish_test.py index 338de3ac..a4765c2f 100644 --- a/test/uts/rest/unit/batch_publish_test.py +++ b/test/uts/rest/unit/batch_publish_test.py @@ -2,7 +2,7 @@ Spec points: RSC22c, RSC22d, BSP2a, BSP2b, BPR2a, BPR2b, BPR2c, BPF2a, BPF2b -NOTE: ably-python has no batch API. `AblyRest` exposes no `batch_publish`, and the package +NOTE: ably-python has no batch API. `DefaultPubSubHttpClient` exposes no `batch_publish`, and the package defines neither `BatchPublishSpec` nor `BatchResult`/`BatchPublishSuccessResult`/ `BatchPublishFailureResult`; the word "batch" appears nowhere under `ably/`. Every test in this file therefore departs from the specification and is gated behind `RUN_DEVIATIONS`. @@ -23,8 +23,8 @@ import msgpack import pytest -from ably.types.message import Message -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/annotations_test.py b/test/uts/rest/unit/channel/annotations_test.py index 1dd1ab57..c3d39264 100644 --- a/test/uts/rest/unit/channel/annotations_test.py +++ b/test/uts/rest/unit/channel/annotations_test.py @@ -15,10 +15,10 @@ import msgpack import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.rest.annotations import RestAnnotations -from ably.types.annotation import Annotation, AnnotationAction -from ably.util.exceptions import AblyException +from ably.pubsub.http.annotations import HttpAnnotations +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient @@ -53,7 +53,7 @@ async def test_rsl10_annotations_attribute_type(): client = rest_client(mock_http) channel = client.channels.get('test-RSL10') - assert isinstance(channel.annotations, RestAnnotations) + assert isinstance(channel.annotations, HttpAnnotations) # UTS: rest/unit/RSAN1c6/publish-post-annotation-create-0 diff --git a/test/uts/rest/unit/channel/get_message_test.py b/test/uts/rest/unit/channel/get_message_test.py index 44838b3e..8f3f1571 100644 --- a/test/uts/rest/unit/channel/get_message_test.py +++ b/test/uts/rest/unit/channel/get_message_test.py @@ -7,8 +7,8 @@ import pytest -from ably.types.message import Message -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/history_test.py b/test/uts/rest/unit/channel/history_test.py index c2fe1047..cfae1d4b 100644 --- a/test/uts/rest/unit/channel/history_test.py +++ b/test/uts/rest/unit/channel/history_test.py @@ -5,8 +5,8 @@ import uuid -from ably.http.paginatedresult import PaginatedResult -from ably.types.message import Message +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.message import Message from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/idempotency_test.py b/test/uts/rest/unit/channel/idempotency_test.py index cb747579..35d8c5c5 100644 --- a/test/uts/rest/unit/channel/idempotency_test.py +++ b/test/uts/rest/unit/channel/idempotency_test.py @@ -8,7 +8,7 @@ import msgpack -from ably.types.message import Message +from ably.pubsub.types.message import Message from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/message_versions_test.py b/test/uts/rest/unit/channel/message_versions_test.py index 2a5c3001..b533c8da 100644 --- a/test/uts/rest/unit/channel/message_versions_test.py +++ b/test/uts/rest/unit/channel/message_versions_test.py @@ -5,8 +5,8 @@ import uuid -from ably.http.paginatedresult import PaginatedResult -from ably.types.message import Message, MessageAction +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.message import Message, MessageAction from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/publish_result_test.py b/test/uts/rest/unit/channel/publish_result_test.py index f7120027..afc5f997 100644 --- a/test/uts/rest/unit/channel/publish_result_test.py +++ b/test/uts/rest/unit/channel/publish_result_test.py @@ -5,8 +5,8 @@ import uuid -from ably.types.message import Message -from ably.types.operations import PublishResult +from ably.pubsub.types.message import Message +from ably.pubsub.types.operations import PublishResult from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/publish_test.py b/test/uts/rest/unit/channel/publish_test.py index f03fb397..7ebef9d4 100644 --- a/test/uts/rest/unit/channel/publish_test.py +++ b/test/uts/rest/unit/channel/publish_test.py @@ -8,8 +8,8 @@ import msgpack import pytest -from ably.types.message import Message -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation, spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/rest_channel_attributes_test.py b/test/uts/rest/unit/channel/rest_channel_attributes_test.py index 7b2b2371..479ebd90 100644 --- a/test/uts/rest/unit/channel/rest_channel_attributes_test.py +++ b/test/uts/rest/unit/channel/rest_channel_attributes_test.py @@ -4,8 +4,8 @@ CHO2, CHO2a, CHM2, CHM2a, CHM2b, CHM2c, CHM2d, CHM2e, CHM2f, CHM2g, CHM2h """ -from ably.types.channeldetails import ChannelDetails -from ably.types.channeloptions import ChannelOptions +from ably.pubsub.types.channeldetails import ChannelDetails +from ably.pubsub.types.channeloptions import ChannelOptions from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channel/update_delete_message_test.py b/test/uts/rest/unit/channel/update_delete_message_test.py index 153cc566..cb4de9c7 100644 --- a/test/uts/rest/unit/channel/update_delete_message_test.py +++ b/test/uts/rest/unit/channel/update_delete_message_test.py @@ -10,9 +10,9 @@ import msgpack import pytest -from ably.types.message import Message -from ably.types.operations import MessageOperation, UpdateDeleteResult -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.types.operations import MessageOperation, UpdateDeleteResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/channels_collection_test.py b/test/uts/rest/unit/channels_collection_test.py index b7517fca..3a43f167 100644 --- a/test/uts/rest/unit/channels_collection_test.py +++ b/test/uts/rest/unit/channels_collection_test.py @@ -8,7 +8,7 @@ import uuid -from ably.rest.channel import Channel, Channels +from ably.pubsub.http.channel import Channel, Channels from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/encoding/message_encoding_test.py b/test/uts/rest/unit/encoding/message_encoding_test.py index 8a86ffc7..6d5d1957 100644 --- a/test/uts/rest/unit/encoding/message_encoding_test.py +++ b/test/uts/rest/unit/encoding/message_encoding_test.py @@ -10,7 +10,7 @@ import msgpack import pytest -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/encoding/msgpack_interop_test.py b/test/uts/rest/unit/encoding/msgpack_interop_test.py index ee681dec..f92d802e 100644 --- a/test/uts/rest/unit/encoding/msgpack_interop_test.py +++ b/test/uts/rest/unit/encoding/msgpack_interop_test.py @@ -10,7 +10,7 @@ import msgpack import pytest -from ably.types.message import Message +from ably.pubsub.types.message import Message FIXTURES_PATH = os.path.join( os.path.dirname(__file__), '..', '..', '..', '..', '..', diff --git a/test/uts/rest/unit/fallback_test.py b/test/uts/rest/unit/fallback_test.py index 31446900..3cc8eafd 100644 --- a/test/uts/rest/unit/fallback_test.py +++ b/test/uts/rest/unit/fallback_test.py @@ -9,7 +9,7 @@ import pytest -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/presence/rest_presence_test.py b/test/uts/rest/unit/presence/rest_presence_test.py index 09a98a3f..584949c8 100644 --- a/test/uts/rest/unit/presence/rest_presence_test.py +++ b/test/uts/rest/unit/presence/rest_presence_test.py @@ -11,10 +11,10 @@ import msgpack import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.types.presence import Presence, PresenceAction, PresenceMessage -from ably.util.crypto import CipherParams -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.presence import Presence, PresenceAction, PresenceMessage +from ably.pubsub.util.crypto import CipherParams +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation, spec_error from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/push/push_admin_publish_test.py b/test/uts/rest/unit/push/push_admin_publish_test.py index aaaec0c1..846baa70 100644 --- a/test/uts/rest/unit/push/push_admin_publish_test.py +++ b/test/uts/rest/unit/push/push_admin_publish_test.py @@ -6,8 +6,8 @@ import msgpack import pytest -from ably.rest.push import Push, PushAdmin, PushChannelSubscriptions, PushDeviceRegistrations -from ably.util.exceptions import AblyException +from ably.pubsub.http.push import Push, PushAdmin, PushChannelSubscriptions, PushDeviceRegistrations +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/push/push_channel_subscriptions_test.py b/test/uts/rest/unit/push/push_channel_subscriptions_test.py index 25ab045c..7826276f 100644 --- a/test/uts/rest/unit/push/push_channel_subscriptions_test.py +++ b/test/uts/rest/unit/push/push_channel_subscriptions_test.py @@ -6,9 +6,9 @@ import msgpack import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.types.channelsubscription import PushChannelSubscription -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.channelsubscription import PushChannelSubscription +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/push/push_channels_test.py b/test/uts/rest/unit/push/push_channels_test.py index b213f534..699dec11 100644 --- a/test/uts/rest/unit/push/push_channels_test.py +++ b/test/uts/rest/unit/push/push_channels_test.py @@ -7,16 +7,16 @@ ``push`` field on a channel) nor LocalDevice (RSH8), so every test in this file is gated behind RUN_DEVIATIONS. The assertions are written against the spec, using the names ably-python would use for these APIs: ``client.device``, -``ably.types.device.LocalDevice`` and ``channel.push.subscribe_device()`` and +``ably.pubsub.types.device.LocalDevice`` and ``channel.push.subscribe_device()`` and friends. Running them raises ImportError/AttributeError until the APIs land. """ import msgpack import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.types.channelsubscription import PushChannelSubscription -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.channelsubscription import PushChannelSubscription +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient @@ -30,7 +30,7 @@ def set_local_device(client, device_id=DEVICE_ID, device_identity_token=DEVICE_IDENTITY_TOKEN, client_id=CLIENT_ID): """Configure the client's local device, standing in for the spec's ``client.device = ...``.""" - from ably.types.device import LocalDevice + from ably.pubsub.types.device import LocalDevice client.device = LocalDevice( id=device_id, diff --git a/test/uts/rest/unit/push/push_device_registrations_test.py b/test/uts/rest/unit/push/push_device_registrations_test.py index ccf7e4a1..495ecc64 100644 --- a/test/uts/rest/unit/push/push_device_registrations_test.py +++ b/test/uts/rest/unit/push/push_device_registrations_test.py @@ -8,9 +8,9 @@ import msgpack import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.types.device import DeviceDetails -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.types.device import DeviceDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/rest_client_test.py b/test/uts/rest/unit/rest_client_test.py index 91731161..fd3454c5 100644 --- a/test/uts/rest/unit/rest_client_test.py +++ b/test/uts/rest/unit/rest_client_test.py @@ -8,9 +8,10 @@ import msgpack import pytest -from ably.rest.auth import Auth -from ably.util.exceptions import AblyException +from ably.pubsub.http.auth import Auth +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client +from test.uts.helpers.deviations import spec_error from test.uts.helpers.mock_http import MockHttpClient SERVER_TIME_MS = 1234567890000 @@ -56,6 +57,10 @@ def on_request(request): # UTS: rest/unit/RSC7d/ably-agent-header-format-0 +# SPEC ERROR RSC7d: the pattern `ably-[a-z]+/` rejects a hyphenated library name. +# RSC7d1 allows any product key, and ably-python reports itself as +# `ably-pubsub-python`. Recorded in deviations.md. Fix the specification first. +@spec_error async def test_rsc7d_ably_agent_header_format(): mock_http = MockHttpClient(on_connection_attempt=connect_successfully) mock_http.queue_response(200, TIME_RESPONSE) diff --git a/test/uts/rest/unit/stats_test.py b/test/uts/rest/unit/stats_test.py index 1e4bd328..0cfdaaa9 100644 --- a/test/uts/rest/unit/stats_test.py +++ b/test/uts/rest/unit/stats_test.py @@ -7,8 +7,8 @@ import pytest -from ably.http.paginatedresult import PaginatedResult -from ably.util.exceptions import AblyException +from ably.pubsub.request.paginatedresult import PaginatedResult +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/time_test.py b/test/uts/rest/unit/time_test.py index ad2d4b6e..b7504395 100644 --- a/test/uts/rest/unit/time_test.py +++ b/test/uts/rest/unit/time_test.py @@ -5,7 +5,7 @@ import pytest -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/types/error_types_test.py b/test/uts/rest/unit/types/error_types_test.py index ae6f0ce6..ef46afec 100644 --- a/test/uts/rest/unit/types/error_types_test.py +++ b/test/uts/rest/unit/types/error_types_test.py @@ -9,7 +9,7 @@ import pytest -from ably.util.exceptions import AblyException +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.deviations import deviation diff --git a/test/uts/rest/unit/types/message_types_test.py b/test/uts/rest/unit/types/message_types_test.py index df2ae6ff..b0db98ac 100644 --- a/test/uts/rest/unit/types/message_types_test.py +++ b/test/uts/rest/unit/types/message_types_test.py @@ -5,7 +5,7 @@ import pytest -from ably.types.message import Message +from ably.pubsub.types.message import Message # `fromEncoded` is spelled `from_encoded` here, and "no encoding" is rendered as the empty # string rather than null, since EncodeDataMixin joins an empty list of transforms. diff --git a/test/uts/rest/unit/types/mutable_message_types_test.py b/test/uts/rest/unit/types/mutable_message_types_test.py index 180e117f..72ee6efb 100644 --- a/test/uts/rest/unit/types/mutable_message_types_test.py +++ b/test/uts/rest/unit/types/mutable_message_types_test.py @@ -4,9 +4,9 @@ MOP2a, MOP2b, MOP2c, UDR1, UDR2, UDR2a, TAN1, TAN2, TAN2a-TAN2l """ -from ably.types.annotation import Annotation, AnnotationAction -from ably.types.message import Message, MessageAction, MessageAnnotations, MessageVersion -from ably.types.operations import MessageOperation, UpdateDeleteResult +from ably.pubsub.types.annotation import Annotation, AnnotationAction +from ably.pubsub.types.message import Message, MessageAction, MessageAnnotations, MessageVersion +from ably.pubsub.types.operations import MessageOperation, UpdateDeleteResult # `fromJson` is spelled `from_encoded` for wire payloads and `from_dict` for plain records, # and `toJson` is spelled `as_dict`. diff --git a/test/uts/rest/unit/types/options_types_test.py b/test/uts/rest/unit/types/options_types_test.py index fb443069..bc5848cd 100644 --- a/test/uts/rest/unit/types/options_types_test.py +++ b/test/uts/rest/unit/types/options_types_test.py @@ -5,11 +5,11 @@ import pytest -from ably import AblyRest -from ably.types.authoptions import AuthOptions -from ably.types.options import Options -from ably.types.tokendetails import TokenDetails -from ably.util.exceptions import AblyException +from ably.pubsub.server import create_http_client +from ably.pubsub.types.authoptions import AuthOptions +from ably.pubsub.types.options import Options +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.deviations import deviation from test.uts.helpers.mock_http import MockHttpClient @@ -191,5 +191,5 @@ async def auth_callback(params): # The missing-credentials check is a constructor argument check, so it raises ValueError # rather than an AblyException with pytest.raises(ValueError) as value_error: - AblyRest() + create_http_client() assert 'key' in str(value_error.value) diff --git a/test/uts/rest/unit/types/paginated_result_test.py b/test/uts/rest/unit/types/paginated_result_test.py index a18525e4..22f0f1bb 100644 --- a/test/uts/rest/unit/types/paginated_result_test.py +++ b/test/uts/rest/unit/types/paginated_result_test.py @@ -5,9 +5,9 @@ import pytest -from ably.types.message import Message -from ably.types.presence import PresenceMessage -from ably.util.exceptions import AblyException +from ably.pubsub.types.message import Message +from ably.pubsub.types.presence import PresenceMessage +from ably.pubsub.util.exceptions import AblyException from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/test/uts/rest/unit/types/presence_message_types_test.py b/test/uts/rest/unit/types/presence_message_types_test.py index be0f044f..7c6b2b7c 100644 --- a/test/uts/rest/unit/types/presence_message_types_test.py +++ b/test/uts/rest/unit/types/presence_message_types_test.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta -from ably.types.presence import PresenceAction, PresenceMessage +from ably.pubsub.types.presence import PresenceAction, PresenceMessage from test.uts.helpers.deviations import deviation, spec_error diff --git a/test/uts/rest/unit/types/token_types_test.py b/test/uts/rest/unit/types/token_types_test.py index bc8764af..243291b3 100644 --- a/test/uts/rest/unit/types/token_types_test.py +++ b/test/uts/rest/unit/types/token_types_test.py @@ -10,8 +10,8 @@ import json -from ably.types.tokendetails import TokenDetails -from ably.types.tokenrequest import TokenRequest +from ably.pubsub.types.tokendetails import TokenDetails +from ably.pubsub.types.tokenrequest import TokenRequest from test.uts.helpers.client import rest_client from test.uts.helpers.mock_http import MockHttpClient diff --git a/uv.lock b/uv.lock index 29ccca45..b8f70971 100644 --- a/uv.lock +++ b/uv.lock @@ -1,29 +1,24 @@ version = 1 revision = 3 -requires-python = ">=3.7" +requires-python = ">=3.8" resolution-markers = [ "python_full_version >= '3.10'", "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", - "python_full_version < '3.8'", + "python_full_version < '3.9'", ] [[package]] -name = "ably" +name = "ably-pubsub-server" version = "3.1.4" source = { editable = "." } dependencies = [ { name = "h2", version = "4.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "h2", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "httpx", version = "0.24.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "httpx", version = "0.28.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, - { name = "msgpack", version = "1.0.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "msgpack", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "httpx" }, + { name = "msgpack", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "msgpack", version = "1.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "pyee", version = "9.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "pyee", version = "13.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, - { name = "websockets", version = "11.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "websockets", version = "13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "pyee" }, + { name = "websockets", version = "13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "websockets", version = "15.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] @@ -32,20 +27,16 @@ crypto = [ { name = "pycryptodome" }, ] dev = [ - { name = "async-case", marker = "python_full_version < '3.8'" }, { name = "importlib-metadata" }, { name = "mock" }, { name = "pytest" }, - { name = "pytest-asyncio", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "pytest-asyncio", version = "0.23.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "pytest-asyncio" }, { name = "pytest-cov" }, { name = "pytest-timeout" }, { name = "pytest-xdist" }, - { name = "respx", version = "0.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "respx", version = "0.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "respx" }, { name = "ruff" }, - { name = "tokenize-rt", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "tokenize-rt", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "tokenize-rt", version = "6.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "tokenize-rt", version = "6.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, { name = "vcdiff-decoder" }, ] @@ -87,36 +78,18 @@ requires-dist = [ ] provides-extras = ["oldcrypto", "crypto", "vcdiff", "dev"] -[[package]] -name = "anyio" -version = "3.7.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.8'" }, - { name = "idna", version = "3.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "sniffio", marker = "python_full_version < '3.8'" }, - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/99/2dfd53fd55ce9838e6ff2d4dac20ce58263798bd1a0dbe18b3a9af3fcfce/anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780", size = 142927, upload-time = "2023-07-05T16:45:02.294Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/24/44299477fe7dcc9cb58d0a57d5a7588d6af2ff403fdd2d47a246c91a3246/anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5", size = 80896, upload-time = "2023-07-05T16:44:59.805Z" }, -] - [[package]] name = "anyio" version = "4.5.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] dependencies = [ - { name = "exceptiongroup", marker = "python_full_version == '3.8.*'" }, - { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, - { name = "sniffio", marker = "python_full_version == '3.8.*'" }, - { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.9'" }, + { name = "idna", marker = "python_full_version < '3.9'" }, + { name = "sniffio", marker = "python_full_version < '3.9'" }, + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/f9/9a7ce600ebe7804daf90d4d48b1c0510a4561ddce43a596be46676f82343/anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b", size = 171293, upload-time = "2024-10-13T22:18:03.307Z" } wheels = [ @@ -133,7 +106,7 @@ resolution-markers = [ ] dependencies = [ { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, - { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "idna", marker = "python_full_version >= '3.9'" }, { name = "sniffio", marker = "python_full_version >= '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, ] @@ -142,12 +115,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] -[[package]] -name = "async-case" -version = "10.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c8/09/87f2a23f5696ac6deb2fff92421f8af46226ea2410d101b453d5aa63e53a/async_case-10.1.0.tar.gz", hash = "sha256:b819f68c78f6c640ab1101ecf69fac189402b490901fa2abc314c48edab5d3da", size = 3668, upload-time = "2022-03-15T21:56:16.795Z" } - [[package]] name = "certifi" version = "2026.1.4" @@ -166,82 +133,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "coverage" -version = "7.2.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/45/8b/421f30467e69ac0e414214856798d4bc32da1336df745e49e49ae5c1e2a8/coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59", size = 762575, upload-time = "2023-05-29T20:08:50.273Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/24/be01e62a7bce89bcffe04729c540382caa5a06bee45ae42136c93e2499f5/coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8", size = 200724, upload-time = "2023-05-29T20:07:03.422Z" }, - { url = "https://files.pythonhosted.org/packages/3d/80/7060a445e1d2c9744b683dc935248613355657809d6c6b2716cdf4ca4766/coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb", size = 201024, upload-time = "2023-05-29T20:07:05.694Z" }, - { url = "https://files.pythonhosted.org/packages/b8/9d/926fce7e03dbfc653104c2d981c0fa71f0572a9ebd344d24c573bd6f7c4f/coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6", size = 229528, upload-time = "2023-05-29T20:07:07.307Z" }, - { url = "https://files.pythonhosted.org/packages/d1/3a/67f5d18f911abf96857f6f7e4df37ca840e38179e2cc9ab6c0b9c3380f19/coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2", size = 227842, upload-time = "2023-05-29T20:07:09.331Z" }, - { url = "https://files.pythonhosted.org/packages/b4/bd/1b2331e3a04f4cc9b7b332b1dd0f3a1261dfc4114f8479bebfcc2afee9e8/coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063", size = 228717, upload-time = "2023-05-29T20:07:11.38Z" }, - { url = "https://files.pythonhosted.org/packages/2b/86/3dbf9be43f8bf6a5ca28790a713e18902b2d884bc5fa9512823a81dff601/coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1", size = 234632, upload-time = "2023-05-29T20:07:13.376Z" }, - { url = "https://files.pythonhosted.org/packages/91/e8/469ed808a782b9e8305a08bad8c6fa5f8e73e093bda6546c5aec68275bff/coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353", size = 232875, upload-time = "2023-05-29T20:07:15.093Z" }, - { url = "https://files.pythonhosted.org/packages/29/8f/4fad1c2ba98104425009efd7eaa19af9a7c797e92d40cd2ec026fa1f58cb/coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495", size = 234094, upload-time = "2023-05-29T20:07:17.013Z" }, - { url = "https://files.pythonhosted.org/packages/94/4e/d4e46a214ae857be3d7dc5de248ba43765f60daeb1ab077cb6c1536c7fba/coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818", size = 203184, upload-time = "2023-05-29T20:07:18.69Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e9/d6730247d8dec2a3dddc520ebe11e2e860f0f98cee3639e23de6cf920255/coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850", size = 204096, upload-time = "2023-05-29T20:07:20.153Z" }, - { url = "https://files.pythonhosted.org/packages/c6/fa/529f55c9a1029c840bcc9109d5a15ff00478b7ff550a1ae361f8745f8ad5/coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f", size = 200895, upload-time = "2023-05-29T20:07:21.963Z" }, - { url = "https://files.pythonhosted.org/packages/67/d7/cd8fe689b5743fffac516597a1222834c42b80686b99f5b44ef43ccc2a43/coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe", size = 201120, upload-time = "2023-05-29T20:07:23.765Z" }, - { url = "https://files.pythonhosted.org/packages/8c/95/16eed713202406ca0a37f8ac259bbf144c9d24f9b8097a8e6ead61da2dbb/coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3", size = 233178, upload-time = "2023-05-29T20:07:25.281Z" }, - { url = "https://files.pythonhosted.org/packages/c1/49/4d487e2ad5d54ed82ac1101e467e8994c09d6123c91b2a962145f3d262c2/coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f", size = 230754, upload-time = "2023-05-29T20:07:27.044Z" }, - { url = "https://files.pythonhosted.org/packages/a7/cd/3ce94ad9d407a052dc2a74fbeb1c7947f442155b28264eb467ee78dea812/coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb", size = 232558, upload-time = "2023-05-29T20:07:28.743Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a8/12cc7b261f3082cc299ab61f677f7e48d93e35ca5c3c2f7241ed5525ccea/coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833", size = 241509, upload-time = "2023-05-29T20:07:30.434Z" }, - { url = "https://files.pythonhosted.org/packages/04/fa/43b55101f75a5e9115259e8be70ff9279921cb6b17f04c34a5702ff9b1f7/coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97", size = 239924, upload-time = "2023-05-29T20:07:32.065Z" }, - { url = "https://files.pythonhosted.org/packages/68/5f/d2bd0f02aa3c3e0311986e625ccf97fdc511b52f4f1a063e4f37b624772f/coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a", size = 240977, upload-time = "2023-05-29T20:07:34.184Z" }, - { url = "https://files.pythonhosted.org/packages/ba/92/69c0722882643df4257ecc5437b83f4c17ba9e67f15dc6b77bad89b6982e/coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a", size = 203168, upload-time = "2023-05-29T20:07:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/b1/96/c12ed0dfd4ec587f3739f53eb677b9007853fd486ccb0e7d5512a27bab2e/coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562", size = 204185, upload-time = "2023-05-29T20:07:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d5/52fa1891d1802ab2e1b346d37d349cb41cdd4fd03f724ebbf94e80577687/coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4", size = 201020, upload-time = "2023-05-29T20:07:38.724Z" }, - { url = "https://files.pythonhosted.org/packages/24/df/6765898d54ea20e3197a26d26bb65b084deefadd77ce7de946b9c96dfdc5/coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4", size = 233994, upload-time = "2023-05-29T20:07:40.274Z" }, - { url = "https://files.pythonhosted.org/packages/15/81/b108a60bc758b448c151e5abceed027ed77a9523ecbc6b8a390938301841/coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01", size = 231358, upload-time = "2023-05-29T20:07:41.998Z" }, - { url = "https://files.pythonhosted.org/packages/61/90/c76b9462f39897ebd8714faf21bc985b65c4e1ea6dff428ea9dc711ed0dd/coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6", size = 233316, upload-time = "2023-05-29T20:07:43.539Z" }, - { url = "https://files.pythonhosted.org/packages/04/d6/8cba3bf346e8b1a4fb3f084df7d8cea25a6b6c56aaca1f2e53829be17e9e/coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d", size = 240159, upload-time = "2023-05-29T20:07:44.982Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ea/4a252dc77ca0605b23d477729d139915e753ee89e4c9507630e12ad64a80/coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de", size = 238127, upload-time = "2023-05-29T20:07:46.522Z" }, - { url = "https://files.pythonhosted.org/packages/9f/5c/d9760ac497c41f9c4841f5972d0edf05d50cad7814e86ee7d133ec4a0ac8/coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d", size = 239833, upload-time = "2023-05-29T20:07:47.992Z" }, - { url = "https://files.pythonhosted.org/packages/69/8c/26a95b08059db1cbb01e4b0e6d40f2e9debb628c6ca86b78f625ceaf9bab/coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511", size = 203463, upload-time = "2023-05-29T20:07:49.939Z" }, - { url = "https://files.pythonhosted.org/packages/b7/00/14b00a0748e9eda26e97be07a63cc911108844004687321ddcc213be956c/coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3", size = 204347, upload-time = "2023-05-29T20:07:51.909Z" }, - { url = "https://files.pythonhosted.org/packages/80/d7/67937c80b8fd4c909fdac29292bc8b35d9505312cff6bcab41c53c5b1df6/coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f", size = 200580, upload-time = "2023-05-29T20:07:54.076Z" }, - { url = "https://files.pythonhosted.org/packages/7a/05/084864fa4bbf8106f44fb72a56e67e0cd372d3bf9d893be818338c81af5d/coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb", size = 226237, upload-time = "2023-05-29T20:07:56.28Z" }, - { url = "https://files.pythonhosted.org/packages/67/a2/6fa66a50e6e894286d79a3564f42bd54a9bd27049dc0a63b26d9924f0aa3/coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9", size = 224256, upload-time = "2023-05-29T20:07:58.189Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c0/73f139794c742840b9ab88e2e17fe14a3d4668a166ff95d812ac66c0829d/coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd", size = 225550, upload-time = "2023-05-29T20:08:00.383Z" }, - { url = "https://files.pythonhosted.org/packages/03/ec/6f30b4e0c96ce03b0e64aec46b4af2a8c49b70d1b5d0d69577add757b946/coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a", size = 232440, upload-time = "2023-05-29T20:08:02.495Z" }, - { url = "https://files.pythonhosted.org/packages/22/c1/2f6c1b6f01a0996c9e067a9c780e1824351dbe17faae54388a4477e6d86f/coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959", size = 230897, upload-time = "2023-05-29T20:08:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d6/53e999ec1bf7498ca4bc5f3b8227eb61db39068d2de5dcc359dec5601b5a/coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02", size = 232024, upload-time = "2023-05-29T20:08:06.031Z" }, - { url = "https://files.pythonhosted.org/packages/e9/40/383305500d24122dbed73e505a4d6828f8f3356d1f68ab6d32c781754b81/coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f", size = 203293, upload-time = "2023-05-29T20:08:07.598Z" }, - { url = "https://files.pythonhosted.org/packages/0e/bc/7e3a31534fabb043269f14fb64e2bb2733f85d4cf39e5bbc71357c57553a/coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0", size = 204040, upload-time = "2023-05-29T20:08:09.919Z" }, - { url = "https://files.pythonhosted.org/packages/c6/fc/be19131010930a6cf271da48202c8cc1d3f971f68c02fb2d3a78247f43dc/coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5", size = 200689, upload-time = "2023-05-29T20:08:11.594Z" }, - { url = "https://files.pythonhosted.org/packages/28/d7/9a8de57d87f4bbc6f9a6a5ded1eaac88a89bf71369bb935dac3c0cf2893e/coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5", size = 200986, upload-time = "2023-05-29T20:08:13.228Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e4/e6182e4697665fb594a7f4e4f27cb3a4dd00c2e3d35c5c706765de8c7866/coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9", size = 230648, upload-time = "2023-05-29T20:08:15.11Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e3/f552d5871943f747165b92a924055c5d6daa164ae659a13f9018e22f3990/coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6", size = 228511, upload-time = "2023-05-29T20:08:16.877Z" }, - { url = "https://files.pythonhosted.org/packages/44/55/49f65ccdd4dfd6d5528e966b28c37caec64170c725af32ab312889d2f857/coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e", size = 229852, upload-time = "2023-05-29T20:08:18.47Z" }, - { url = "https://files.pythonhosted.org/packages/0d/31/340428c238eb506feb96d4fb5c9ea614db1149517f22cc7ab8c6035ef6d9/coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050", size = 235578, upload-time = "2023-05-29T20:08:20.298Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ce/97c1dd6592c908425622fe7f31c017d11cf0421729b09101d4de75bcadc8/coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5", size = 234079, upload-time = "2023-05-29T20:08:22.365Z" }, - { url = "https://files.pythonhosted.org/packages/de/a3/5a98dc9e239d0dc5f243ef5053d5b1bdcaa1dee27a691dfc12befeccf878/coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f", size = 234991, upload-time = "2023-05-29T20:08:24.974Z" }, - { url = "https://files.pythonhosted.org/packages/4a/fb/78986d3022e5ccf2d4370bc43a5fef8374f092b3c21d32499dee8e30b7b6/coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e", size = 203160, upload-time = "2023-05-29T20:08:26.701Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1c/6b3c9c363fb1433c79128e0d692863deb761b1b78162494abb9e5c328bc0/coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c", size = 204085, upload-time = "2023-05-29T20:08:28.146Z" }, - { url = "https://files.pythonhosted.org/packages/88/da/495944ebf0ad246235a6bd523810d9f81981f9b81c6059ba1f56e943abe0/coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9", size = 200725, upload-time = "2023-05-29T20:08:29.851Z" }, - { url = "https://files.pythonhosted.org/packages/ca/0c/3dfeeb1006c44b911ee0ed915350db30325d01808525ae7cc8d57643a2ce/coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2", size = 201022, upload-time = "2023-05-29T20:08:31.429Z" }, - { url = "https://files.pythonhosted.org/packages/61/af/5964b8d7d9a5c767785644d9a5a63cacba9a9c45cc42ba06d25895ec87be/coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7", size = 229102, upload-time = "2023-05-29T20:08:32.982Z" }, - { url = "https://files.pythonhosted.org/packages/d9/1d/cd467fceb62c371f9adb1d739c92a05d4e550246daa90412e711226bd320/coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e", size = 227441, upload-time = "2023-05-29T20:08:35.044Z" }, - { url = "https://files.pythonhosted.org/packages/fe/57/e4f8ad64d84ca9e759d783a052795f62a9f9111585e46068845b1cb52c2b/coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1", size = 228265, upload-time = "2023-05-29T20:08:36.861Z" }, - { url = "https://files.pythonhosted.org/packages/88/8b/b0d9fe727acae907fa7f1c8194ccb6fe9d02e1c3e9001ecf74c741f86110/coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9", size = 234217, upload-time = "2023-05-29T20:08:38.837Z" }, - { url = "https://files.pythonhosted.org/packages/66/2e/c99fe1f6396d93551aa352c75410686e726cd4ea104479b9af1af22367ce/coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250", size = 232466, upload-time = "2023-05-29T20:08:40.768Z" }, - { url = "https://files.pythonhosted.org/packages/bb/e9/88747b40c8fb4a783b40222510ce6d66170217eb05d7f46462c36b4fa8cc/coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2", size = 233669, upload-time = "2023-05-29T20:08:42.944Z" }, - { url = "https://files.pythonhosted.org/packages/b1/d5/a8e276bc005e42114468d4fe03e0a9555786bc51cbfe0d20827a46c1565a/coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb", size = 203199, upload-time = "2023-05-29T20:08:44.734Z" }, - { url = "https://files.pythonhosted.org/packages/a9/0c/4a848ae663b47f1195abcb09a951751dd61f80b503303b9b9d768e0fd321/coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27", size = 204109, upload-time = "2023-05-29T20:08:46.417Z" }, - { url = "https://files.pythonhosted.org/packages/67/fb/b3b1d7887e1ea25a9608b0776e480e4bbc303ca95a31fd585555ec4fff5a/coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d", size = 193207, upload-time = "2023-05-29T20:08:48.153Z" }, -] - [[package]] name = "coverage" version = "7.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791, upload-time = "2024-08-04T19:45:30.9Z" } wheels = [ @@ -539,8 +436,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } @@ -548,56 +444,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] -[[package]] -name = "execnet" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/c8/d382dc7a1e68a165f4a4ab612a08b20d8534a7d20cc590630b734ca0c54b/execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af", size = 161098, upload-time = "2023-07-09T17:14:03.468Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/9c/a079946da30fac4924d92dbc617e5367d454954494cf1e71567bcc4e00ee/execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", size = 37097, upload-time = "2023-07-09T17:14:01.888Z" }, -] - [[package]] name = "execnet" version = "2.1.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, ] -[[package]] -name = "h11" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418, upload-time = "2022-09-25T15:40:01.519Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259, upload-time = "2022-09-25T15:39:59.68Z" }, -] - [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, @@ -608,8 +467,7 @@ name = "h2" version = "4.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", - "python_full_version < '3.8'", + "python_full_version < '3.9'", ] dependencies = [ { name = "hpack", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, @@ -642,8 +500,7 @@ name = "hpack" version = "4.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", - "python_full_version < '3.8'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/3e/9b/fda93fb4d957db19b0f6b370e79d586b3e8528b20252c729c476a2c02954/hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095", size = 49117, upload-time = "2020-08-30T10:35:57.868Z" } wheels = [ @@ -663,75 +520,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, ] -[[package]] -name = "httpcore" -version = "0.17.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "anyio", version = "3.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "certifi", marker = "python_full_version < '3.8'" }, - { name = "h11", version = "0.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "sniffio", marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/ad/c98ecdbfe04417e71e143bf2f2fb29128e4787d78d1cedba21bd250c7e7a/httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888", size = 62676, upload-time = "2023-07-05T12:09:31.29Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/2c/2bde7ff8dd2064395555220cbf7cba79991172bf5315a07eb3ac7688d9f1/httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87", size = 74513, upload-time = "2023-07-05T12:09:29.425Z" }, -] - [[package]] name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] dependencies = [ - { name = "certifi", marker = "python_full_version >= '3.8'" }, - { name = "h11", version = "0.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "certifi" }, + { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] -[[package]] -name = "httpx" -version = "0.24.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version < '3.8'" }, - { name = "httpcore", version = "0.17.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "idna", version = "3.10", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "sniffio", marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f8/2a/114d454cb77657dbf6a293e69390b96318930ace9cd96b51b99682493276/httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd", size = 81858, upload-time = "2023-05-19T00:50:56.678Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/91/e41f64f03d2a13aee7e8c819d82ee3aa7cdc484d18c0ae859742597d5aa0/httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd", size = 75377, upload-time = "2023-05-19T00:50:54.91Z" }, -] - [[package]] name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] dependencies = [ - { name = "anyio", version = "4.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "anyio", version = "4.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "anyio", version = "4.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "certifi", marker = "python_full_version >= '3.8'" }, - { name = "httpcore", version = "1.0.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, - { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ @@ -743,8 +554,7 @@ name = "hyperframe" version = "6.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", - "python_full_version < '3.8'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/5a/2a/4747bff0a17f7281abe73e955d60d80aae537a5d203f417fa1c2e7578ebb/hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914", size = 25008, upload-time = "2021-04-17T12:11:22.757Z" } wheels = [ @@ -764,27 +574,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, -] - [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, @@ -795,9 +588,7 @@ name = "importlib-metadata" version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "zipp", version = "3.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "zipp", version = "3.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "zipp", version = "3.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "zipp", version = "3.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/12/ab288357b884ebc807e3f4eff63ce5ba6b941ba61499071bf19f1bbc7f7f/importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d", size = 50445, upload-time = "2022-10-01T17:09:15.698Z" } @@ -805,25 +596,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/98/c277899f5aa21f6e6946e1c83f2af650cbfee982763ffb91db07ff7d3a13/importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116", size = 23010, upload-time = "2022-10-01T17:09:13.903Z" }, ] -[[package]] -name = "iniconfig" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646, upload-time = "2023-01-07T11:08:11.254Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892, upload-time = "2023-01-07T11:08:09.864Z" }, -] - [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ @@ -851,76 +630,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/03/b7e605db4a57c0f6fba744b11ef3ddf4ddebcada35022927a2b5fc623fdf/mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", size = 28536, upload-time = "2020-12-10T07:33:11.564Z" }, ] -[[package]] -name = "msgpack" -version = "1.0.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/a1/eba11a0d4b764bc62966a565b470f8c6f38242723ba3057e9b5098678c30/msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c", size = 127834, upload-time = "2023-03-08T17:50:48.564Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/4a/36d936e54cf71e23ad276564465f6a54fb129e3d61520b76e13e0bb29167/msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9", size = 129738, upload-time = "2023-03-08T17:49:18.464Z" }, - { url = "https://files.pythonhosted.org/packages/f2/da/770118f8d48e11cc9a2c7cb60d7d3c8016266526bd42c6ff5bd21013d099/msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198", size = 74671, upload-time = "2023-03-08T17:49:20.311Z" }, - { url = "https://files.pythonhosted.org/packages/73/99/f338ce8b69e934c04e5d9187f85de1ae395882cd56e7deb48e78a1749af8/msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81", size = 70230, upload-time = "2023-03-08T17:49:21.958Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/bc7fdb75a35bf32c7c529c247dcadfd0502aac2309e207a89b0be6fe42ea/msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7", size = 309410, upload-time = "2023-03-08T17:49:23.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/67/f992ada3b42889f1b984e5651d63ea21ca3a92049cff6d75fe0a4a63e422/msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3", size = 316846, upload-time = "2023-03-08T17:49:24.786Z" }, - { url = "https://files.pythonhosted.org/packages/10/fe/9e004c4deb457f1ef1ad88c1188da5691ff1855e0d03a5ac3635ae1f6530/msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b", size = 311396, upload-time = "2023-03-08T17:49:26.075Z" }, - { url = "https://files.pythonhosted.org/packages/95/c9/560c3203c4327881c9f2de26c42dacdd9567bfe7fa43458e2a680c4bdcaf/msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c", size = 311165, upload-time = "2023-03-08T17:49:27.494Z" }, - { url = "https://files.pythonhosted.org/packages/10/ca/50c3a5e92d459a942169747315afd8c226d05427eccff903ddf33135c574/msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd", size = 348664, upload-time = "2023-03-08T17:49:28.736Z" }, - { url = "https://files.pythonhosted.org/packages/29/56/1fb6b96aab759ab3bc05b03ba6d936b350db72aac203cde56ea6bd001237/msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a", size = 316731, upload-time = "2023-03-08T17:49:30.041Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/b47f9e93fc381885624c40cbbbd0480b18ae11ca588162fe724d43495372/msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea", size = 57134, upload-time = "2023-03-08T17:49:31.365Z" }, - { url = "https://files.pythonhosted.org/packages/3c/e5/3d436bed11849ba05d777ed3fd1a0440170bad460335ea541dd6946047ed/msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a", size = 61631, upload-time = "2023-03-08T17:49:32.482Z" }, - { url = "https://files.pythonhosted.org/packages/27/ad/4edfe383ec3185611441179ffee8cbc8155d7575fbad73f6d31015e35451/msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0", size = 127502, upload-time = "2023-03-08T17:49:33.974Z" }, - { url = "https://files.pythonhosted.org/packages/c1/57/01f2d8805160f559ec21d095fc7576a26fbaed2475af24ce4a135c380c14/msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898", size = 73747, upload-time = "2023-03-08T17:49:35.158Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fe/8a7747ca57074307a2e8f1de58441952a9dbdf9e8a8e5873d53a5ce0835c/msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a", size = 69041, upload-time = "2023-03-08T17:49:36.44Z" }, - { url = "https://files.pythonhosted.org/packages/33/0a/aa7b53ae17cf1dc1c352d705ab3162fc572c55048cc3177c1a88009c47fd/msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a", size = 316114, upload-time = "2023-03-08T17:49:38.252Z" }, - { url = "https://files.pythonhosted.org/packages/6b/6d/de239d77d347f1990c41b4800075a15e06f748186dd120166270dd071734/msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705", size = 325080, upload-time = "2023-03-08T17:49:39.528Z" }, - { url = "https://files.pythonhosted.org/packages/7e/1c/9d0fd241a4e88e1cd2f5babea4a27ac25b1b86dbbc05fa10741e82079a93/msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d", size = 319393, upload-time = "2023-03-08T17:49:40.755Z" }, - { url = "https://files.pythonhosted.org/packages/b8/bc/1d5fe4732dc78ff86aaf677596da08f0ae736e60ca8ab49c1f1c7366cb1a/msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9", size = 316118, upload-time = "2023-03-08T17:49:41.964Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e9/c79ecc36cfa34d850a01773565e0fccafd69efff07172028c3a5f758b83f/msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7", size = 354984, upload-time = "2023-03-08T17:49:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/45/85/6b55b0cabad846d3e730226a897f878f8f63ee505668bb6c55a697b0bfb0/msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed", size = 323580, upload-time = "2023-03-08T17:49:44.71Z" }, - { url = "https://files.pythonhosted.org/packages/0e/69/3d10e741dd2bbb806af5cdc76551735baab5f5f9773701eb05502c913a6e/msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c", size = 56419, upload-time = "2023-03-08T17:49:46.603Z" }, - { url = "https://files.pythonhosted.org/packages/6b/79/0dec8f035160464ca88b221cc79691a71cf88dc25207c17f1d918b2c7bb0/msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2", size = 60781, upload-time = "2023-03-08T17:49:47.912Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c1/1b591574ba71481fbf38359a8fca5108e4ad130a6dbb9b2acb3e9277d0fe/msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c", size = 72520, upload-time = "2023-03-08T17:50:02.199Z" }, - { url = "https://files.pythonhosted.org/packages/62/57/170af6c6fccd2d950ea01e1faa58cae9643226fa8705baded11eca3aa8b5/msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b", size = 289288, upload-time = "2023-03-08T17:50:04.213Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c4/f2c8695ae69d1425eddc5e2f849c525b562dc8409bc2979e525f3dd4fecd/msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f", size = 299695, upload-time = "2023-03-08T17:50:05.622Z" }, - { url = "https://files.pythonhosted.org/packages/62/5c/9c7fed4ca0235a2d7b8d15b4047c328976b97d2b227719e54cad1e47c244/msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f", size = 293149, upload-time = "2023-03-08T17:50:06.915Z" }, - { url = "https://files.pythonhosted.org/packages/ef/13/c110d89d5079169354394dc226e6f84d818722939bc1fe3f9c25f982e903/msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d", size = 292899, upload-time = "2023-03-08T17:50:08.215Z" }, - { url = "https://files.pythonhosted.org/packages/72/ac/2eda5af7cd1450c52d031e48c76b280eac5bb2e588678876612f95be34ab/msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086", size = 334408, upload-time = "2023-03-08T17:50:10Z" }, - { url = "https://files.pythonhosted.org/packages/e8/60/78906f564804aae23eb1102eca8b8830f1e08a649c179774c05fa7dc0aad/msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf", size = 302791, upload-time = "2023-03-08T17:50:11.414Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b8/89cb1809b076a4651169851aa1f98128b75cbfe14034b914c9040b13c4cf/msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77", size = 57095, upload-time = "2023-03-08T17:50:12.741Z" }, - { url = "https://files.pythonhosted.org/packages/e8/1f/be19c9c9cfdcc2ae8ee8c65dbe5f281cc1f3331f9b9523735f39b090b448/msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82", size = 62112, upload-time = "2023-03-08T17:50:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f7/df5814697c25bdebb14ea97d27ddca04f5d4c6e249f096d086fea521c139/msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c", size = 126923, upload-time = "2023-03-08T17:50:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/33/52/099f0dde1283bac7bf267ab941dfa3b7c89ee701e4252973f8d3c10e68d6/msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d", size = 73246, upload-time = "2023-03-08T17:50:16.487Z" }, - { url = "https://files.pythonhosted.org/packages/f1/1f/cc3e8274934c8323f6106dae22cba8bad413166f4efb3819573de58c215c/msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb", size = 68947, upload-time = "2023-03-08T17:50:17.767Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/2c3b443df88f5d400f2e19a3d867564d004b26e137f18c2f2663913987bc/msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba", size = 313568, upload-time = "2023-03-08T17:50:19.016Z" }, - { url = "https://files.pythonhosted.org/packages/0a/04/bc319ba061f6dc9077745988be288705b3f9f18c5a209772a3e8fcd419fd/msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1", size = 322443, upload-time = "2023-03-08T17:50:20.341Z" }, - { url = "https://files.pythonhosted.org/packages/2f/21/e488871f8e498efe14821b0c870eb95af52cfafb9b8dd41d83fad85b383b/msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87", size = 315490, upload-time = "2023-03-08T17:50:22.301Z" }, - { url = "https://files.pythonhosted.org/packages/28/8f/c58c53c884217cc572c19349c7e1129b5a6eae36df0a017aae3a8f3d7aa8/msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb", size = 324288, upload-time = "2023-03-08T17:50:23.739Z" }, - { url = "https://files.pythonhosted.org/packages/0d/90/44edef4a8c6f035b054c4b017c5adcb22a35ec377e17e50dd5dced279a6b/msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48", size = 361405, upload-time = "2023-03-08T17:50:24.992Z" }, - { url = "https://files.pythonhosted.org/packages/56/50/bfcc0fad07067b6f1b09d940272ec749d5fe82570d938c2348c3ad0babf7/msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0", size = 329585, upload-time = "2023-03-08T17:50:26.387Z" }, - { url = "https://files.pythonhosted.org/packages/80/f0/c1fadb4e4a38fda19e35b1b6f887d72cc9c57778af43b53f64a8cd62e922/msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e", size = 57668, upload-time = "2023-03-08T17:50:28.037Z" }, - { url = "https://files.pythonhosted.org/packages/da/46/855bdcbf004fd87b6a4451e8dcd61329439dcd9039887f71ca5085769216/msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1", size = 62509, upload-time = "2023-03-08T17:50:29.85Z" }, - { url = "https://files.pythonhosted.org/packages/12/6e/0cfd1dc07f61a6ac606587a393f489c3ca463469d285a73c8e5e2f61b021/msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025", size = 130498, upload-time = "2023-03-08T17:50:31.551Z" }, - { url = "https://files.pythonhosted.org/packages/4b/3d/cc5eb6d69e0ecde80a78cc42f48579971ec333e509d56a4a6de1a2c40ba2/msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5", size = 75178, upload-time = "2023-03-08T17:50:32.78Z" }, - { url = "https://files.pythonhosted.org/packages/bf/68/032e62ad44f92ba6a4ae7c45054843cdec7f0c405ecdfd166f25123b0c47/msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd", size = 70460, upload-time = "2023-03-08T17:50:34.57Z" }, - { url = "https://files.pythonhosted.org/packages/49/57/a28120d82f8e77622a1e1efc652389c71145f6b89b47b39814a7c6038373/msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437", size = 313499, upload-time = "2023-03-08T17:50:36.329Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ff/ca74e519c47139b6c08fb21db5ead2bd2eed6cb1225f9be69390cdb48182/msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f", size = 322301, upload-time = "2023-03-08T17:50:38.097Z" }, - { url = "https://files.pythonhosted.org/packages/43/87/6507d56f62b958d822ae4ffe1c4507ed7d3cf37ad61114665816adcf4adc/msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282", size = 316630, upload-time = "2023-03-08T17:50:39.397Z" }, - { url = "https://files.pythonhosted.org/packages/67/f8/e3ab674f4a945308362e9342297fe6b35a89dd0f648aa325aabffa5dc210/msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d", size = 316251, upload-time = "2023-03-08T17:50:41.153Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f1/45b73a9e97f702bcb5f51569b93990e456bc969363e55122374c22ed7d24/msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8", size = 352781, upload-time = "2023-03-08T17:50:42.435Z" }, - { url = "https://files.pythonhosted.org/packages/17/10/be97811782473d709d07b65a3955a5a76d47686aff3d62bb41d48aea7c92/msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11", size = 321996, upload-time = "2023-03-08T17:50:43.726Z" }, - { url = "https://files.pythonhosted.org/packages/18/3f/3860151fbdf50e369bbe4ffd307a588417669c725025e383f3ce5893690f/msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc", size = 57827, upload-time = "2023-03-08T17:50:45.517Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fa/3ca00fb1e53bcacf8c186fa6aff2d2086862b12e289bcf38227d9d40bd86/msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164", size = 62775, upload-time = "2023-03-08T17:50:47.305Z" }, -] - [[package]] name = "msgpack" version = "1.1.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/45/b1/ea4f68038a18c77c9467400d166d74c4ffa536f34761f7983a104357e614/msgpack-1.1.1.tar.gz", hash = "sha256:77b79ce34a2bdab2594f490c8e80dd62a02d650b91a75159a63ec413b8d104cd", size = 173555, upload-time = "2025-06-13T06:52:51.324Z" } wheels = [ @@ -1057,53 +772,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/a7/b1992b4fb3da3b413f5fb78a63bad42f256c3be2352eb69273c3789c2c96/msgpack-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:67016ae8c8965124fdede9d3769528ad8284f14d635337ffa6a713a580f6c030", size = 71540, upload-time = "2025-10-08T09:15:55.573Z" }, ] -[[package]] -name = "packaging" -version = "24.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", size = 147882, upload-time = "2024-03-10T09:39:28.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", size = 53488, upload-time = "2024-03-10T09:39:25.947Z" }, -] - [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] -[[package]] -name = "pluggy" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/42/8f2833655a29c4e9cb52ee8a2be04ceac61bcff4a680fb338cbd3d1e322d/pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3", size = 61613, upload-time = "2023-06-21T09:12:28.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/32/4a79112b8b87b21450b066e102d6608907f4c885ed7b04c3fdb085d4d6ae/pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", size = 17695, upload-time = "2023-06-21T09:12:27.397Z" }, -] - [[package]] name = "pluggy" version = "1.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" } wheels = [ @@ -1178,32 +861,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/62/2392b7879f4d2c1bfa20815720b89d464687877851716936b9609959c201/pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6", size = 1802461, upload-time = "2025-05-17T17:21:41.722Z" }, ] -[[package]] -name = "pyee" -version = "9.1.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2c/ebe4fd8213b3d720b193a62f07169607e945dd02a08edc45b28ca52fbe07/pyee-9.1.1.tar.gz", hash = "sha256:a1ebcd38d92e7d780635ab3442c0f6ce49840d9bfe0b0549921a6188860af0db", size = 22634, upload-time = "2023-06-09T06:13:29.141Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/53/39b67ce3841a5bb2d444f64ed969fb79ebd5bfed6867c3f88f3916407270/pyee-9.1.1-py2.py3-none-any.whl", hash = "sha256:f4a9853503d2f5a69d4350b54ba70841ebc535c53ebfaaa40c0fb47e63e78b3e", size = 15073, upload-time = "2023-06-09T06:13:27.255Z" }, -] - [[package]] name = "pyee" version = "13.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] dependencies = [ - { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37", size = 31250, upload-time = "2025-03-17T18:53:15.955Z" } @@ -1218,50 +881,24 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.8'" }, - { name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8' and python_full_version < '3.10'" }, + { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging", version = "24.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "packaging", version = "25.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, - { name = "pluggy", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "packaging" }, + { name = "pluggy", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "pluggy", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "tomli", version = "2.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "tomli", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8' and python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, ] -[[package]] -name = "pytest-asyncio" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "pytest", marker = "python_full_version < '3.8'" }, - { name = "typing-extensions", version = "4.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/53/57663d99acaac2fcdafdc697e52a9b1b7d6fcf36616281ff9768a44e7ff3/pytest_asyncio-0.21.2.tar.gz", hash = "sha256:d67738fc232b94b326b9d060750beb16e0074210b98dd8b58a5239fa2a154f45", size = 30656, upload-time = "2024-04-29T13:23:24.738Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/ce/1e4b53c213dce25d6e8b163697fbce2d43799d76fa08eea6ad270451c370/pytest_asyncio-0.21.2-py3-none-any.whl", hash = "sha256:ab664c88bb7998f711d8039cacd4884da6430886ae8bbd4eded552ed2004f16b", size = 13368, upload-time = "2024-04-29T13:23:23.126Z" }, -] - [[package]] name = "pytest-asyncio" version = "0.23.8" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] dependencies = [ - { name = "pytest", marker = "python_full_version >= '3.8'" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/de/b4/0b378b7bf26a8ae161c3890c0b48a91a04106c5713ce81b4b080ea2f4f18/pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3", size = 46920, upload-time = "2024-07-17T17:39:34.617Z" } wheels = [ @@ -1273,8 +910,7 @@ name = "pytest-cov" version = "2.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage", version = "7.2.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "coverage", version = "7.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.8.*'" }, + { name = "coverage", version = "7.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, { name = "coverage", version = "7.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pytest" }, @@ -1315,8 +951,7 @@ name = "pytest-xdist" version = "1.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "execnet", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, - { name = "execnet", version = "2.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "execnet" }, { name = "pytest" }, { name = "pytest-forked" }, { name = "six" }, @@ -1326,32 +961,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/fc/30821e7799bddd56989523ee003cde488c6e6053dfd29ba07db2ba934a04/pytest_xdist-1.34.0-py2.py3-none-any.whl", hash = "sha256:ba5d10729372d65df3ac150872f9df5d2ed004a3b0d499cc0164aafedd8c7b66", size = 36841, upload-time = "2020-07-27T23:05:23.851Z" }, ] -[[package]] -name = "respx" -version = "0.20.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -dependencies = [ - { name = "httpx", version = "0.24.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e6/0b/e0df26ea5c7145d95f1ab8ecb20f0778dd8af718e56747977dca9d28362a/respx-0.20.2.tar.gz", hash = "sha256:07cf4108b1c88b82010f67d3c831dae33a375c7b436e54d87737c7f9f99be643", size = 26080, upload-time = "2023-07-20T23:01:23.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/47/8c5a8b02c2144770fe353585b6db21e392c4318b8cff897738159feff562/respx-0.20.2-py2.py3-none-any.whl", hash = "sha256:ab8e1cf6da28a5b2dd883ea617f8130f77f676736e6e9e4a25817ad116a172c9", size = 22849, upload-time = "2023-07-20T23:01:21.994Z" }, -] - [[package]] name = "respx" version = "0.22.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] dependencies = [ - { name = "httpx", version = "0.28.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.8'" }, + { name = "httpx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } wheels = [ @@ -1402,24 +1017,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] -[[package]] -name = "tokenize-rt" -version = "5.0.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/40/01/fb40ea8c465f680bf7aa3f5bee39c62ba8b7f52c38048c27aa95aff4f779/tokenize_rt-5.0.0.tar.gz", hash = "sha256:3160bc0c3e8491312d0485171dea861fc160a240f5f5766b72a1165408d10740", size = 5329, upload-time = "2022-10-03T23:28:00.861Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/12/4c7495f25b4c9131706f3aaffb185d4de32c02a6ee49d875e929c5b7c919/tokenize_rt-5.0.0-py2.py3-none-any.whl", hash = "sha256:c67772c662c6b3dc65edf66808577968fb10badfc2042e3027196bed4daf9e5a", size = 5848, upload-time = "2022-10-03T23:27:59.459Z" }, -] - [[package]] name = "tokenize-rt" version = "6.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/7d/09/6257dabdeab5097d72c5d874f29b33cd667ec411af6667922d84f85b79b5/tokenize_rt-6.0.0.tar.gz", hash = "sha256:b9711bdfc51210211137499b5e355d3de5ec88a85d2025c520cbb921b5194367", size = 5360, upload-time = "2024-08-04T21:01:19.405Z" } wheels = [ @@ -1448,27 +1051,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, ] -[[package]] -name = "tomli" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164, upload-time = "2022-02-08T10:54:04.006Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757, upload-time = "2022-02-08T10:54:02.017Z" }, -] - [[package]] name = "tomli" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version == '3.9.*'", - "python_full_version == '3.8.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, @@ -1514,24 +1100,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] -[[package]] -name = "typing-extensions" -version = "4.7.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876, upload-time = "2023-07-02T14:20:55.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", size = 33232, upload-time = "2023-07-02T14:20:53.275Z" }, -] - [[package]] name = "typing-extensions" version = "4.13.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } wheels = [ @@ -1560,92 +1134,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/81/5aace343f579809a50e152eac66458ad159c4fae9e06bef438ddb0ebaf92/vcdiff_decoder-0.2.0-py3-none-any.whl", hash = "sha256:0be7163871d0c79fb57b0c9f0a994ee151c4e348a442d3d508da6975983b91aa", size = 19277, upload-time = "2026-09-23T13:05:57.346Z" }, ] -[[package]] -name = "websockets" -version = "11.0.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/3b/2ed38e52eed4cf277f9df5f0463a99199a04d9e29c9e227cfafa57bd3993/websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016", size = 104235, upload-time = "2023-05-07T14:25:20.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/76/88640f8aeac7eb0d058b913e7bb72682f8d569db44c7d30e576ec4777ce1/websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac", size = 123714, upload-time = "2023-05-07T14:23:15.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/6b/26b28115b46e23e74ede76d95792eedfe8c58b21f4daabfff1e9f159c8fe/websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d", size = 120949, upload-time = "2023-05-07T14:23:17.656Z" }, - { url = "https://files.pythonhosted.org/packages/f3/82/2d1f3395d47fab65fa8b801e2251b324300ed8db54753b6fb7919cef0c11/websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f", size = 121032, upload-time = "2023-05-07T14:23:20.096Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ec/56bdd12d847e4fc2d0a7ba2d7f1476f79cda50599d11ffb6080b86f21ef1/websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564", size = 130620, upload-time = "2023-05-07T14:23:21.545Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fb/ae5ed4be3514287cf8f6c348c87e1392a6e3f4d6eadae75c18847a2f84b6/websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11", size = 129628, upload-time = "2023-05-07T14:23:23.105Z" }, - { url = "https://files.pythonhosted.org/packages/58/0a/7570e15661a0a546c3a1152d95fe8c05480459bab36247f0acbf41f01a41/websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca", size = 129938, upload-time = "2023-05-07T14:23:24.959Z" }, - { url = "https://files.pythonhosted.org/packages/ba/6c/5c0322b2875e8395e6bf0eff11f43f3e25da7ef5b12f4d908cd3a19ea841/websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54", size = 134663, upload-time = "2023-05-07T14:23:26.382Z" }, - { url = "https://files.pythonhosted.org/packages/de/0e/d7274e4d41d7b34f204744c27a23707be2ecefaf6f7df2145655f086ecd7/websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4", size = 133900, upload-time = "2023-05-07T14:23:28.307Z" }, - { url = "https://files.pythonhosted.org/packages/82/3c/00f051abcf88aec5e952a8840076749b0b26a30c219dcae8ba70200998aa/websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526", size = 134520, upload-time = "2023-05-07T14:23:30.734Z" }, - { url = "https://files.pythonhosted.org/packages/8f/7b/4d4ecd29be7d08486e38f987a6603c491296d1e33fe55127d79aebb0333e/websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69", size = 124152, upload-time = "2023-05-07T14:23:33.183Z" }, - { url = "https://files.pythonhosted.org/packages/98/a7/0ed69892981351e5acf88fac0ff4c801fabca2c3bdef9fca4c7d3fde8c53/websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f", size = 124674, upload-time = "2023-05-07T14:23:35.331Z" }, - { url = "https://files.pythonhosted.org/packages/16/49/ae616bd221efba84a3d78737b417f704af1ffa36f40dcaba5eb954dd4753/websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb", size = 123748, upload-time = "2023-05-07T14:23:37.977Z" }, - { url = "https://files.pythonhosted.org/packages/0a/84/68b848a373493b58615d6c10e9e8ccbaadfd540f84905421739a807704f8/websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288", size = 120975, upload-time = "2023-05-07T14:23:40.339Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a8/e81533499f84ef6cdd95d11d5b05fa827c0f097925afd86f16e6a2631d8e/websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d", size = 121017, upload-time = "2023-05-07T14:23:41.874Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ca/65d6986665888494eca4d5435a9741c822022996f0f4200c57ce4b9242f7/websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3", size = 131200, upload-time = "2023-05-07T14:23:43.309Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/a8a582ebeeecc8b5f332997d44c57e241748f8a9856e06a38a5a13b30796/websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b", size = 130195, upload-time = "2023-05-07T14:23:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5e/b25c60067d700e811dccb4e3c318eeadd3a19d8b3620de9f97434af777a7/websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6", size = 130569, upload-time = "2023-05-07T14:23:46.926Z" }, - { url = "https://files.pythonhosted.org/packages/14/fc/5cbbf439c925e1e184a0392ec477a30cee2fabc0e63807c1d4b6d570fb52/websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97", size = 136015, upload-time = "2023-05-07T14:23:48.43Z" }, - { url = "https://files.pythonhosted.org/packages/0f/d8/a997d3546aef9cc995a1126f7d7ade96c0e16c1a0efb9d2d430aee57c925/websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf", size = 135292, upload-time = "2023-05-07T14:23:50.744Z" }, - { url = "https://files.pythonhosted.org/packages/89/8f/707a05d5725f956c78d252a5fd73b89fa3ac57dd3959381c2d1acb41cb13/websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd", size = 135890, upload-time = "2023-05-07T14:23:52.707Z" }, - { url = "https://files.pythonhosted.org/packages/b5/94/ac47552208583d5dbcce468430c1eb2ae18962f6b3a694a2b7727cc60d4a/websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c", size = 124149, upload-time = "2023-05-07T14:23:53.848Z" }, - { url = "https://files.pythonhosted.org/packages/e1/7c/0ad6e7ef0a054d73092f616d20d3d9bd3e1b837554cb20a52d8dd9f5b049/websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8", size = 124670, upload-time = "2023-05-07T14:23:55.812Z" }, - { url = "https://files.pythonhosted.org/packages/b5/a8/8900184ab0b06b6e620ba7e92cf2faa5caa9ba86e148541b8fff1c7b6646/websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152", size = 120868, upload-time = "2023-05-07T14:23:57.24Z" }, - { url = "https://files.pythonhosted.org/packages/44/a8/66c3a66b70b01a6c55fde486298766177fa11dd0d3a2c1cfc6820f25b4dc/websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f", size = 130557, upload-time = "2023-05-07T14:23:59.274Z" }, - { url = "https://files.pythonhosted.org/packages/70/fc/71377f36ef3049f3bc7db7c0f3a7696929d5f836d7a18777131d994192a9/websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b", size = 129640, upload-time = "2023-05-07T14:24:01.412Z" }, - { url = "https://files.pythonhosted.org/packages/36/19/0da435afb26a6c47c0c045a82e414912aa2ac10de5721276a342bd9fdfee/websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb", size = 129903, upload-time = "2023-05-07T14:24:02.872Z" }, - { url = "https://files.pythonhosted.org/packages/38/ed/b8b133416536b6816e480594864e5950051db522714623eefc9e5275ec04/websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007", size = 135302, upload-time = "2023-05-07T14:24:04.326Z" }, - { url = "https://files.pythonhosted.org/packages/e9/26/1dfaa81788f61c485b4d65f1b28a19615e39f9c45100dce5e2cbf5ad1352/websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0", size = 134562, upload-time = "2023-05-07T14:24:05.829Z" }, - { url = "https://files.pythonhosted.org/packages/a7/f7/1e852351e8073c32885172a6bef64c95d14c13ff3634b01d4a1086321491/websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af", size = 135191, upload-time = "2023-05-07T14:24:07.659Z" }, - { url = "https://files.pythonhosted.org/packages/19/d3/2ea3f95d83033675144b0848a0ae2e4998b3f763da09ec3df6bce97ea4e6/websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f", size = 124138, upload-time = "2023-05-07T14:24:09.697Z" }, - { url = "https://files.pythonhosted.org/packages/94/8c/266155c14b7a26deca6fa4c4d5fd15b0ab32725d78a2acfcf6b24943585d/websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de", size = 124672, upload-time = "2023-05-07T14:24:12.595Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/60eccd7e9703bbe93fc4167d1e7ada7e8e8e51544122198d63fd8e3460b7/websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0", size = 123706, upload-time = "2023-05-07T14:24:14.633Z" }, - { url = "https://files.pythonhosted.org/packages/d1/ec/7e2b9bebc2e9b4a48404144106bbc6a7ace781feeb0e6a3829551e725fa5/websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae", size = 120944, upload-time = "2023-05-07T14:24:16.144Z" }, - { url = "https://files.pythonhosted.org/packages/8a/bd/a5e5973899d78d44a540f50a9e30b01c6771e8bf7883204ee762060cf95a/websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99", size = 121030, upload-time = "2023-05-07T14:24:17.905Z" }, - { url = "https://files.pythonhosted.org/packages/ec/3f/0c5cae14e9e86401105833383405787ae4caddd476a8fc5561259253dab7/websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa", size = 130811, upload-time = "2023-05-07T14:24:21.175Z" }, - { url = "https://files.pythonhosted.org/packages/a0/39/acc3d4b15c5207ef7cca823c37eca8c74e3e1a1a63a397798986be3bdef7/websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86", size = 129876, upload-time = "2023-05-07T14:24:22.498Z" }, - { url = "https://files.pythonhosted.org/packages/58/05/2efb520317340ece74bfc4d88e8f011dd71a4e6c263000bfffb71a343685/websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c", size = 130158, upload-time = "2023-05-07T14:24:25.193Z" }, - { url = "https://files.pythonhosted.org/packages/30/a5/d641f2a9a4b4079cfddbb0726fc1b914be76a610aaedb45e4760899a4ce1/websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0", size = 134494, upload-time = "2023-05-07T14:24:26.463Z" }, - { url = "https://files.pythonhosted.org/packages/ca/20/25211be61d50189650fb0ec6084b6d6339f5c7c6436a6c217608dcb553e4/websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e", size = 133735, upload-time = "2023-05-07T14:24:29.598Z" }, - { url = "https://files.pythonhosted.org/packages/c6/91/f36454b87edf10a95be9c7212d2dcb8c606ddbf7a183afdc498933acdd19/websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788", size = 134368, upload-time = "2023-05-07T14:24:30.923Z" }, - { url = "https://files.pythonhosted.org/packages/58/68/9403771de1b1c21a2e878e4841815af8c9f8893b094654934e2a5ee4dbc8/websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74", size = 124148, upload-time = "2023-05-07T14:24:32.299Z" }, - { url = "https://files.pythonhosted.org/packages/25/25/48540419005d07ed2d368a7eafb44ed4f33a2691ae4c210850bf31123c4a/websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f", size = 124665, upload-time = "2023-05-07T14:24:34.482Z" }, - { url = "https://files.pythonhosted.org/packages/c0/21/cb9dfbbea8dc0ad89ced52630e7e61edb425fb9fdc6002f8d0c5dd26b94b/websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8", size = 123707, upload-time = "2023-05-07T14:24:36.007Z" }, - { url = "https://files.pythonhosted.org/packages/8f/f2/8a3eb016be19743c7eb9e67c855df0fdfa5912534ffaf83a05b62667d761/websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd", size = 120963, upload-time = "2023-05-07T14:24:37.478Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1a/3da73e69ebc00649d11ed836541c92c1a2df0b8a8aa641a2c8746e7c2b9c/websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016", size = 121014, upload-time = "2023-05-07T14:24:39.009Z" }, - { url = "https://files.pythonhosted.org/packages/d9/36/5741e62ccf629c8e38cc20f930491f8a33ce7dba972cae93dba3d6f02552/websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61", size = 130408, upload-time = "2023-05-07T14:24:40.825Z" }, - { url = "https://files.pythonhosted.org/packages/66/89/799f595c67b97a8a17e13d2764e088f631616bd95668aaa4c04b7cada136/websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b", size = 129407, upload-time = "2023-05-07T14:24:42.479Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9c/2356ecb952fd3992b73f7a897d65e57d784a69b94bb8d8fd5f97531e5c02/websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd", size = 129712, upload-time = "2023-05-07T14:24:44.186Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f5/15998b164c183af0513bba744b51ecb08d396ff86c0db3b55d62624d1f15/websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7", size = 134386, upload-time = "2023-05-07T14:24:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/a04d2911f6e2b9e781ce7ffc1e8516b54b85f985369eec8c853fd619d8e8/websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1", size = 133639, upload-time = "2023-05-07T14:24:46.966Z" }, - { url = "https://files.pythonhosted.org/packages/72/89/0d150939f2e592ed78c071d69237ac1c872462cc62a750c5f592f3d4ab18/websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311", size = 134260, upload-time = "2023-05-07T14:24:48.633Z" }, - { url = "https://files.pythonhosted.org/packages/9a/6e/0fd7274042f46acb589161407f4b505b44c68d369437ce919bae1fa9b8c4/websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128", size = 124146, upload-time = "2023-05-07T14:24:50.566Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3f/65dfa50084a06ab0a05f3ca74195c2c17a1c075b8361327d831ccce0a483/websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e", size = 124665, upload-time = "2023-05-07T14:24:52.111Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2f/3ad8ac4a9dc9d685e098e534180a36ed68fe2e85e82e225e00daec86bb94/websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf", size = 120795, upload-time = "2023-05-07T14:24:53.421Z" }, - { url = "https://files.pythonhosted.org/packages/a7/8c/7100e9cf310fe1d83d1ae1322203f4eb2b767a7c2b301c1e70db6270306f/websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5", size = 122910, upload-time = "2023-05-07T14:24:54.851Z" }, - { url = "https://files.pythonhosted.org/packages/78/b2/df5452031b02b857851139806308f2af7c749069e25bfe15f2d559ade6e7/websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998", size = 122516, upload-time = "2023-05-07T14:24:56.477Z" }, - { url = "https://files.pythonhosted.org/packages/03/28/3a51ffcf51ac45746639f83128908bbb1cd212aa631e42d15a7acebce5cb/websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b", size = 122462, upload-time = "2023-05-07T14:24:57.77Z" }, - { url = "https://files.pythonhosted.org/packages/eb/fb/2af7fc3ce2c3f1378d48a15802b4ff2caf6c0dfac13291e73c557caf04f7/websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb", size = 124704, upload-time = "2023-05-07T14:24:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/20/62/5c6039c4069912adb27889ddd000403a2de9e0fe6aebe439b4e6b128a6b8/websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20", size = 120795, upload-time = "2023-05-07T14:25:01.047Z" }, - { url = "https://files.pythonhosted.org/packages/38/30/01a10fbf4cc1e7ffa07be9b0401501918fc9433d71fb7da4cfcef3bd26ca/websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931", size = 122908, upload-time = "2023-05-07T14:25:02.734Z" }, - { url = "https://files.pythonhosted.org/packages/99/23/43071c989c0f87f612e7bccee98d00b04bddd3aca0cdc1ffaf31f6f8a4b4/websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9", size = 122515, upload-time = "2023-05-07T14:25:04.803Z" }, - { url = "https://files.pythonhosted.org/packages/b6/96/0d586c25d043aeab9457dad8e407251e3baf314d871215f91847e7b995c4/websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280", size = 122465, upload-time = "2023-05-07T14:25:06.352Z" }, - { url = "https://files.pythonhosted.org/packages/27/e9/605b0618d0864e9be7c2a78f22bff57aba9cf56b9fccde3205db9023ae22/websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b", size = 124707, upload-time = "2023-05-07T14:25:07.782Z" }, - { url = "https://files.pythonhosted.org/packages/1b/3d/3dc77699fa4d003f2e810c321592f80f62b81d7b78483509de72ffe581fd/websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82", size = 120795, upload-time = "2023-05-07T14:25:09.785Z" }, - { url = "https://files.pythonhosted.org/packages/a6/1b/5c83c40f8d3efaf0bb2fdf05af94fb920f74842b7aaf31d7598e3ee44d58/websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c", size = 122909, upload-time = "2023-05-07T14:25:11.243Z" }, - { url = "https://files.pythonhosted.org/packages/32/2c/ab8ea64e9a7d8bf62a7ea7a037fb8d328d8bd46dbfe083787a9d452a148e/websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d", size = 122517, upload-time = "2023-05-07T14:25:12.881Z" }, - { url = "https://files.pythonhosted.org/packages/8b/97/34178f5f7c29e679372d597cebfeff2aa45991d741d938117d4616e81a74/websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4", size = 122463, upload-time = "2023-05-07T14:25:15.154Z" }, - { url = "https://files.pythonhosted.org/packages/ed/45/466944e00b324ae3a1fddb305b4abf641f582e131548f07bcd970971b154/websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602", size = 124707, upload-time = "2023-05-07T14:25:17.112Z" }, - { url = "https://files.pythonhosted.org/packages/47/96/9d5749106ff57629b54360664ae7eb9afd8302fad1680ead385383e33746/websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6", size = 118056, upload-time = "2023-05-07T14:25:18.508Z" }, -] - [[package]] name = "websockets" version = "13.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549, upload-time = "2024-09-21T17:34:21.54Z" } wheels = [ @@ -1816,24 +1310,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] -[[package]] -name = "zipp" -version = "3.15.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.8'", -] -sdist = { url = "https://files.pythonhosted.org/packages/00/27/f0ac6b846684cecce1ee93d32450c45ab607f65c2e0255f0092032d91f07/zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", size = 18454, upload-time = "2023-02-25T02:17:22.503Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/fa/c9e82bbe1af6266adf08afb563905eb87cab83fde00a0a08963510621047/zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556", size = 6758, upload-time = "2023-02-25T02:17:20.807Z" }, -] - [[package]] name = "zipp" version = "3.20.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.8.*'", + "python_full_version < '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199, upload-time = "2024-09-13T13:44:16.101Z" } wheels = [