diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ea00dc..f002eda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - client: ['node', 'python', 'node-protocolv2'] + client: ['node', 'python', 'node-protocolv2', 'python-protocolv2'] server: ['node', 'python', 'node-protocolv2'] # protocolv2 clients aren't bakcwards compatible with protocolv1 servers exclude: @@ -48,6 +48,10 @@ jobs: server: 'python' - client: 'node-protocolv2' server: 'node' + - client: 'python-protocolv2' + server: 'python' + - client: 'python-protocolv2' + server: 'node' runs-on: ubuntu-latest permissions: contents: write diff --git a/Makefile b/Makefile index 073be5e..8e873b3 100644 --- a/Makefile +++ b/Makefile @@ -34,3 +34,13 @@ schema.json: codegen-python: schema.json cd impls/python; \ ./generate_client.sh + +codegen-python-v2: + cd impls/python-protocolv2; \ + ./generate_client.sh + +start-node-protocolv2: + cd impls/node-protocolv2 && \ + npm ci && \ + HEARTBEAT_MS=500 PORT=9887 SERVER_TRANSPORT_ID=server \ + npm run --silent start:server diff --git a/impls/python-protocolv2/README.md b/impls/python-protocolv2/README.md new file mode 100644 index 0000000..e69de29 diff --git a/impls/python-protocolv2/client.dockerfile b/impls/python-protocolv2/client.dockerfile new file mode 100644 index 0000000..cfa989f --- /dev/null +++ b/impls/python-protocolv2/client.dockerfile @@ -0,0 +1,18 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.11-slim-bookworm + +WORKDIR /usr/src/river + +RUN apt-get update && apt-get install -fy git +RUN pip install uv==0.6.8 + +COPY pyproject.toml . +COPY uv.lock . +COPY README.md . +COPY src src + +RUN uv sync + +# bash is required for "time" in python:3.11-slim-bookworm +CMD ["bash", "-c", "time timeout 120 uv run python -u -m testservice.client --log-cli-level=debug"] diff --git a/impls/python-protocolv2/generate_client.sh b/impls/python-protocolv2/generate_client.sh new file mode 100755 index 0000000..9e00c10 --- /dev/null +++ b/impls/python-protocolv2/generate_client.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -ex + +REPO_ROOT_DIR="$(git rev-parse --show-toplevel)" + +cd "${REPO_ROOT_DIR}/impls/python-protocolv2" + +rm -rf \ + src/river_python_test/protos/*pb2* \ + src/river_python_test/protos/service_river.py \ + || true +git clean -fdx src/river_python_test/protos/ || true + +mkdir -p src/river_python_test/protos + +uv run python -m grpc_tools.protoc \ + --proto_path="${REPO_ROOT_DIR}/protos" \ + --python_out=./src \ + --mypy_out=./src \ + --grpc_python_out=./src \ + --mypy_grpc_out=./src \ + "${REPO_ROOT_DIR}/protos/testservice/protos/service.proto" + +uv run python -m replit_river.codegen \ + server \ + --module testservice.protos \ + --output ./src/testservice/protos \ + "${REPO_ROOT_DIR}/protos/testservice/protos/service.proto" + +uv run python -m replit_river.codegen \ + client \ + --output ./src/testservice/protos \ + --client-name TestCient \ + --protocol-version v2.0 \ + "${REPO_ROOT_DIR}/schema-v2.json" + +"${REPO_ROOT_DIR}/scripts/patch-grpc.sh" "$(pwd)" + +if ! uv run ruff check --fix; then + uv run ruff check --add-noqa +fi + +uv run ruff format +uv run pyright . + +echo "Completed" diff --git a/impls/python-protocolv2/pyproject.toml b/impls/python-protocolv2/pyproject.toml new file mode 100644 index 0000000..05ec5e3 --- /dev/null +++ b/impls/python-protocolv2/pyproject.toml @@ -0,0 +1,66 @@ +[project] +name = "river-python-test" +version = "1.0.0" +description = "River toolkit for Python" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "replit-river==0.17.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/testservice"] + +[dependency-groups] +dev = [ + "deptry>=0.23.0", + "mypy>=1.15.0", + "mypy-protobuf>=3.6.0", + "pyright>=1.1.396", + "pytest>=8.3.5", + "pytest-asyncio>=0.25.3", + "pytest-cov>=4.1.0", + "pytest-mock>=3.14.0", + "ruff>=0.11.0", + "types-protobuf>=5.29.1.20250315", +] + +[tool.ruff] +lint.select = ["F", "E", "W", "I001"] + +# Should be kept in sync with mypy.ini in the project root. +# The VSCode mypy extension can only read /mypy.ini. +# While mypy run inside the chat container can only see this file. +[tool.mypy] +plugins = "pydantic.mypy" +disallow_untyped_defs = true +warn_return_any = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" # auto-detect async tests/fixtures +addopts = "--tb=short" +env = [ + "DD_DOGSTATSD_DISABLE=true", + "DD_TRACE_ENABLED=false", +] +filterwarnings = [ + "ignore::DeprecationWarning", # google SDKs cause this noise +] +markers = [ + "e2e: marks tests as end-to-end (deselect with '-m \"not e2e\"')", +] + +[[tool.mypy.overrides]] +module = [ + "google.auth.*", + "google.oauth2.*", + "google.cloud.sqlalchemy_spanner.sqlalchemy_spanner.*", + "grpc.*", + "grpc_tools.*", + "nanoid.*", +] +ignore_missing_imports = true diff --git a/impls/python-protocolv2/src/testservice/client.py b/impls/python-protocolv2/src/testservice/client.py new file mode 100644 index 0000000..72bc19d --- /dev/null +++ b/impls/python-protocolv2/src/testservice/client.py @@ -0,0 +1,297 @@ +import asyncio +import json +import logging +import os +import sys +from datetime import timedelta +from typing import Any, AsyncGenerator, AsyncIterable, AsyncIterator, Dict + +from replit_river import RiverError +from replit_river.error_schema import RiverError # noqa: F811 +from replit_river.transport_options import TransportOptions, UriAndMetadata +from replit_river.v2 import Client + +from testservice.protos import TestCient +from testservice.protos.kv.set import SetInit +from testservice.protos.kv.watch import WatchInit, WatchOutput +from testservice.protos.repeat.echo import EchoInit, EchoInput, EchoOutput +from testservice.protos.repeat.echo_prefix import ( + Echo_PrefixInit, + Echo_PrefixInput, + Echo_PrefixOutput, +) +from testservice.protos.upload.send import SendInit, SendInput, SendOutput + +# TODO: note:numbers +# Unfortunately we've got to work around a difference in interpretation between node +# and python. In node, `number` is a float and that is that. +# +# When rendered to schema.json we also get `"type": "number"`, which means in Python we +# get `float`. +# +# This is fine, except that Node renders 42.0f as `42`, whereas python does `42.0`. +# +# Because of this, where we should have strict equivalence, we now introduce `{v:.0f}`, +# a subtle and unfortunate difference. + +# Load environment variables +PORT = os.environ["PORT"] +CLIENT_TRANSPORT_ID = os.environ["CLIENT_TRANSPORT_ID"] +SERVER_TRANSPORT_ID = os.environ["SERVER_TRANSPORT_ID"] +HEARTBEAT_MS = int(os.environ["HEARTBEAT_MS"]) +HEARTBEATS_UNTIL_DEAD = int(os.environ["HEARTBEATS_UNTIL_DEAD"]) +SESSION_DISCONNECT_GRACE_MS = int(os.environ["SESSION_DISCONNECT_GRACE_MS"]) +RIVER_SERVER = os.environ["RIVER_SERVER"] + + +logging.basicConfig( + level=logging.DEBUG, + format="Python Client %(asctime)s - %(levelname)s - %(message)s", +) + + +input_streams: Dict[str, asyncio.Queue] = {} +tasks: Dict[str, asyncio.Task] = {} + + +async def asyncly_emit( + actions: list[dict[Any, Any]], +) -> AsyncGenerator[tuple[str, Any], None]: # noqa: E501 + for action in actions: + line = json.dumps(action) + yield line, action + await asyncio.sleep(5) + + +async def read_from_stdin() -> AsyncGenerator[tuple[str, Any], None]: + while True: + line = await asyncio.get_event_loop().run_in_executor(None, sys.stdin.readline) + + # We're done + if not line: + break + + try: + action = json.loads(line) + except json.JSONDecodeError as e: + # Sometimes docker injects this into the stream: + # {"hijack":true,"stream":true,"stdin":true,"stdout":true,"stderr":true}{"type": "invoke", ... # noqa: E501 + offset = e.colno - 1 + first = json.loads(line[0:offset]) + if "hijack" not in first: + logging.warning("Working around docker 'hijack' message") + raise + action = json.loads(line[offset:]) + + yield line, action + + +async def process_commands(static_actions: list[dict[Any, Any]] | None) -> None: + logging.info("start python river client") + uri = f"ws://{RIVER_SERVER}:{PORT}" + logging.info( + "Heartbeat: %d ms, Heartbeats to dead: %d, Session disconnect grace: %d ms", + HEARTBEAT_MS, + HEARTBEATS_UNTIL_DEAD, + SESSION_DISCONNECT_GRACE_MS, + ) + + async def get_connection_metadata() -> UriAndMetadata[None]: + return { + "uri": uri, + "metadata": None, + } + + assert CLIENT_TRANSPORT_ID + assert SERVER_TRANSPORT_ID + client = Client( + get_connection_metadata, + client_id=CLIENT_TRANSPORT_ID, + server_id=SERVER_TRANSPORT_ID, + transport_options=TransportOptions( + heartbeat_ms=HEARTBEAT_MS, + heartbeats_until_dead=HEARTBEATS_UNTIL_DEAD, + session_disconnect_grace_ms=SESSION_DISCONNECT_GRACE_MS, + ), + ) + test_client = TestCient(client) + try: + if static_actions: + actions = asyncly_emit(static_actions) + else: + actions = read_from_stdin() + + async for line, action in actions: + if not line: + break + + if not action: + print("FATAL: invalid command", line) + sys.exit(1) + + # Extract the named groups + id_ = action["id"] + payload = action.get("payload") + + # Example handling for a 'kv.set' command + match action["proc"]: + case "kv.set": + k = payload["k"] + v = payload["v"] + try: + res = await test_client.kv.set( + SetInit(k=k, v=int(v)), timedelta(seconds=60) + ) + print( + f"{id_} -- ok:{res.v:.0f}" + ) # TODO: See `note:numbers` above + except Exception: + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during kv.set") + case "kv.watch": + k = payload["k"] + tasks[id_] = asyncio.create_task(handle_watch(id_, k, test_client)) + case "repeat.echo": + if id_ not in input_streams: + input_streams[id_] = asyncio.Queue() + tasks[id_] = asyncio.create_task( + handle_echo(id_, action["init"], test_client) + ) # noqa: E501 + else: + s = payload["s"] + await input_streams[id_].put(s) + case "repeat.echo_prefix": + async def input_iterator() -> AsyncGenerator[ + Echo_PrefixInput, None + ]: + while inputs := input_streams.get(id_): + yield Echo_PrefixInput(str=await inputs.get()) + + async def do_call(id_: str, init: dict[Any, Any]) -> None: + try: + async for v in await test_client.repeat.echo_prefix( + Echo_PrefixInit(**init), input_iterator() + ): + match v: + case Echo_PrefixOutput(): + print(f"{id_} -- ok:{v.out}") + case RiverError(): + print(f"{id_} -- err:{v.code}") + except Exception: + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during repeat.echo_prefix") + + if id_ not in input_streams: + input_streams[id_] = asyncio.Queue() + init = action["init"] + tasks[id_] = asyncio.create_task(do_call(id_, init)) + else: + await input_streams[id_].put(payload["str"]) + case "upload.send": + if id_ not in input_streams: + input_streams[id_] = asyncio.Queue() + tasks[id_] = asyncio.create_task( + handle_upload(id_, test_client) + ) + + if payload is not None: + # For UploadNoInit + await input_streams[id_].put(payload["part"]) + else: + part = payload["part"] + await input_streams[id_].put(part) + + if part == "EOF": + # Wait for the upload task to complete once EOF is sent + await tasks[id_] + tasks.pop(id_, None) # Cleanup task reference + input_streams.pop(id_, None) # Cleanup queue reference + case other: + raise ValueError("Unexpected action!", other) + except Exception: + logging.exception("Top-level exception") + finally: + logging.info("Input finished, closing") + await client.close() + for task in tasks.values(): + task.cancel() + while not task.done(): + await asyncio.sleep(0.1) + exception = task.exception() + if exception is not None: + logging.error("Task raised an exception: {}", exception) + tasks.clear() + + +async def handle_watch( + id_: str, + k: str, + test_client: TestCient, +) -> None: + try: + async for v in await test_client.kv.watch(WatchInit(k=k)): + if isinstance(v, WatchOutput): + print(f"{id_} -- ok:{v.v:.0f}") # TODO: See `note:numbers` above + else: + print(f"{id_} -- err:{v.code}") + except Exception: + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during handle_watch") + + +async def handle_upload(id_: str, test_client: TestCient) -> None: + async def upload_iterator() -> AsyncIterator[SendInput]: + while stream := input_streams.get(id_): + item = await stream.get() + if item == "EOF": # Use a special EOF marker to break the loop + break + yield SendInput(part=item) + + async def print_result(result: SendOutput | RiverError) -> None: + if isinstance(result, SendOutput): + print(f"{id_} -- ok:{result.doc}") + else: # Assuming this handles both RiverError and exceptions + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during handle_upload") + return + + try: + init = SendInit() + result = await test_client.upload.send(init, upload_iterator()) + await print_result(result) + except Exception: + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during handle_upload") + + +async def handle_echo(id_: str, init: Any, test_client: TestCient) -> None: + def print_result(result: EchoOutput | RiverError) -> None: + if isinstance(result, EchoOutput): + print(f"{id_} -- ok:{result.out}") + else: # Assuming this handles both RiverError and exceptions + print(f"{id_} -- err:{result.code}") + + async def serve_inputs() -> AsyncIterable[EchoInput]: + while inputs := input_streams.get(id_): + yield EchoInput(str=await inputs.get()) + + try: + async for v in await test_client.repeat.echo(EchoInit(**init), serve_inputs()): + print_result(v) + except Exception: + print(f"{id_} -- err:UNEXPECTED_DISCONNECT") + logging.exception("Error during handle_echo") + + +async def main() -> None: + # static_actions = [ + # {"type":"invoke","id":"1","proc":"repeat.echo","init":{}}, + # {"type":"invoke","id":"1","proc":"repeat.echo","payload":{"s":"hello"}}, + # {"type":"invoke","id":"1","proc":"repeat.echo","payload":{"s":"world"}}, + # ] + # await process_commands(static_actions) + await process_commands(None) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/impls/python-protocolv2/src/testservice/protos/__init__.py b/impls/python-protocolv2/src/testservice/protos/__init__.py new file mode 100644 index 0000000..7d32ce0 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/__init__.py @@ -0,0 +1,16 @@ +# Code generated by river.codegen. DO NOT EDIT. +from typing import Literal + +import replit_river as river +from pydantic import BaseModel # noqa: F401 + +from .kv import KvService +from .repeat import RepeatService +from .upload import UploadService + + +class TestCient: + def __init__(self, client: river.v2.Client[Literal[None]]): + self.kv = KvService(client) + self.repeat = RepeatService(client) + self.upload = UploadService(client) diff --git a/impls/python-protocolv2/src/testservice/protos/kv/__init__.py b/impls/python-protocolv2/src/testservice/protos/kv/__init__.py new file mode 100644 index 0000000..958b3a0 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/kv/__init__.py @@ -0,0 +1,59 @@ +# Code generated by river.codegen. DO NOT EDIT. +import datetime +from collections.abc import AsyncIterable, AsyncIterator # noqa: F401 +from typing import Any + +import replit_river as river +from pydantic import TypeAdapter # noqa: F401 +from replit_river.error_schema import RiverError, RiverErrorTypeAdapter + +from .set import SetInit, SetInitTypeAdapter, SetOutput, SetOutputTypeAdapter +from .watch import ( + WatchErrors, + WatchErrorsTypeAdapter, + WatchInit, + WatchInitTypeAdapter, + WatchOutput, + WatchOutputTypeAdapter, +) + + +class KvService: + def __init__(self, client: river.v2.Client[Any]): + self.client = client + + async def set( + self, + init: SetInit, + timeout: datetime.timedelta, + ) -> SetOutput: + return await self.client.send_rpc( + "kv", + "set", + init, + lambda x: SetInitTypeAdapter.validate_python(x), + lambda x: SetOutputTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: RiverErrorTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + timeout, + ) + + async def watch( + self, + init: WatchInit, + ) -> AsyncIterator[WatchOutput | WatchErrors | RiverError]: + return self.client.send_subscription( + "kv", + "watch", + init, + lambda x: WatchInitTypeAdapter.validate_python(x), + lambda x: WatchOutputTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: WatchErrorsTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + ) diff --git a/impls/python-protocolv2/src/testservice/protos/kv/set.py b/impls/python-protocolv2/src/testservice/protos/kv/set.py new file mode 100644 index 0000000..c69d257 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/kv/set.py @@ -0,0 +1,18 @@ +# Code generated by river.codegen. DO NOT EDIT. + +from pydantic import BaseModel, TypeAdapter + + +class SetInit(BaseModel): + k: str + v: float + + +class SetOutput(BaseModel): + v: int + + +SetOutputTypeAdapter: TypeAdapter[SetOutput] = TypeAdapter(SetOutput) + + +SetInitTypeAdapter: TypeAdapter[SetInit] = TypeAdapter(SetInit) diff --git a/impls/python-protocolv2/src/testservice/protos/kv/watch.py b/impls/python-protocolv2/src/testservice/protos/kv/watch.py new file mode 100644 index 0000000..6ae3a02 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/kv/watch.py @@ -0,0 +1,29 @@ +# Code generated by river.codegen. DO NOT EDIT. +from typing import ( + Literal, +) + +from pydantic import BaseModel, TypeAdapter +from replit_river.error_schema import RiverError + + +class WatchInit(BaseModel): + k: str + + +class WatchOutput(BaseModel): + v: float + + +WatchOutputTypeAdapter: TypeAdapter[WatchOutput] = TypeAdapter(WatchOutput) + + +class WatchErrors(RiverError): + code: Literal["NOT_FOUND"] + message: str + + +WatchErrorsTypeAdapter: TypeAdapter[WatchErrors] = TypeAdapter(WatchErrors) + + +WatchInitTypeAdapter: TypeAdapter[WatchInit] = TypeAdapter(WatchInit) diff --git a/impls/python-protocolv2/src/testservice/protos/repeat/__init__.py b/impls/python-protocolv2/src/testservice/protos/repeat/__init__.py new file mode 100644 index 0000000..ee1ecbc --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/repeat/__init__.py @@ -0,0 +1,78 @@ +# Code generated by river.codegen. DO NOT EDIT. +import datetime # noqa: F401 +from collections.abc import AsyncIterable, AsyncIterator +from typing import Any + +import replit_river as river +from pydantic import TypeAdapter # noqa: F401 +from replit_river.error_schema import RiverError, RiverErrorTypeAdapter + +from .echo import ( + EchoInit, + EchoInitTypeAdapter, + EchoInput, + EchoInputTypeAdapter, + EchoOutput, + EchoOutputTypeAdapter, +) +from .echo_prefix import ( + Echo_PrefixInit, + Echo_PrefixInitTypeAdapter, + Echo_PrefixInput, + Echo_PrefixInputTypeAdapter, + Echo_PrefixOutput, + Echo_PrefixOutputTypeAdapter, +) + + +class RepeatService: + def __init__(self, client: river.v2.Client[Any]): + self.client = client + + async def echo( + self, + init: EchoInit, + inputStream: AsyncIterable[EchoInput], + ) -> AsyncIterator[EchoOutput | RiverError | RiverError]: + return self.client.send_stream( + "repeat", + "echo", + init, + inputStream, + lambda x: EchoInitTypeAdapter.validate_python(x), + lambda x: EchoInputTypeAdapter.dump_python( + x, # type: ignore[arg-type] + by_alias=True, + exclude_none=True, + ), + lambda x: EchoOutputTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: RiverErrorTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + ) + + async def echo_prefix( + self, + init: Echo_PrefixInit, + inputStream: AsyncIterable[Echo_PrefixInput], + ) -> AsyncIterator[Echo_PrefixOutput | RiverError | RiverError]: + return self.client.send_stream( + "repeat", + "echo_prefix", + init, + inputStream, + lambda x: Echo_PrefixInitTypeAdapter.validate_python(x), + lambda x: Echo_PrefixInputTypeAdapter.dump_python( + x, # type: ignore[arg-type] + by_alias=True, + exclude_none=True, + ), + lambda x: Echo_PrefixOutputTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: RiverErrorTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + ) diff --git a/impls/python-protocolv2/src/testservice/protos/repeat/echo.py b/impls/python-protocolv2/src/testservice/protos/repeat/echo.py new file mode 100644 index 0000000..af5167f --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/repeat/echo.py @@ -0,0 +1,24 @@ +# Code generated by river.codegen. DO NOT EDIT. + +from pydantic import BaseModel, TypeAdapter + + +class EchoInit(BaseModel): + pass + + +class EchoInput(BaseModel): + str: str + + +class EchoOutput(BaseModel): + out: str + + +EchoOutputTypeAdapter: TypeAdapter[EchoOutput] = TypeAdapter(EchoOutput) + + +EchoInitTypeAdapter: TypeAdapter[EchoInit] = TypeAdapter(EchoInit) + + +EchoInputTypeAdapter: TypeAdapter[EchoInput] = TypeAdapter(EchoInput) diff --git a/impls/python-protocolv2/src/testservice/protos/repeat/echo_prefix.py b/impls/python-protocolv2/src/testservice/protos/repeat/echo_prefix.py new file mode 100644 index 0000000..5761df9 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/repeat/echo_prefix.py @@ -0,0 +1,28 @@ +# Code generated by river.codegen. DO NOT EDIT. + +from pydantic import BaseModel, TypeAdapter + + +class Echo_PrefixInit(BaseModel): + prefix: str + + +class Echo_PrefixInput(BaseModel): + str: str + + +class Echo_PrefixOutput(BaseModel): + out: str + + +Echo_PrefixOutputTypeAdapter: TypeAdapter[Echo_PrefixOutput] = TypeAdapter( + Echo_PrefixOutput +) + + +Echo_PrefixInitTypeAdapter: TypeAdapter[Echo_PrefixInit] = TypeAdapter(Echo_PrefixInit) + + +Echo_PrefixInputTypeAdapter: TypeAdapter[Echo_PrefixInput] = TypeAdapter( + Echo_PrefixInput +) diff --git a/impls/python-protocolv2/src/testservice/protos/service_pb2.py b/impls/python-protocolv2/src/testservice/protos/service_pb2.py new file mode 100644 index 0000000..b9bed80 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/service_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: testservice/protos/service.proto +# Protobuf Python Version: 5.29.0 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version # type: ignore +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, 5, 29, 0, "", "testservice/protos/service.proto" +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n testservice/protos/service.proto\x12\x11replit.river.test"!\n\tKVRequest\x12\t\n\x01k\x18\x01 \x01(\t\x12\t\n\x01v\x18\x02 \x01(\x02"\x17\n\nKVResponse\x12\t\n\x01v\x18\x01 \x01(\x02"\x18\n\tEchoInput\x12\x0b\n\x03str\x18\x01 \x01(\t"\x19\n\nEchoOutput\x12\x0b\n\x03out\x18\x01 \x01(\t"\x1b\n\x0bUploadInput\x12\x0c\n\x04part\x18\x01 \x01(\t"\x1b\n\x0cUploadOutput\x12\x0b\n\x03\x64oc\x18\x01 \x01(\t2\x90\x01\n\x02kv\x12\x42\n\x03set\x12\x1c.replit.river.test.KVRequest\x1a\x1d.replit.river.test.KVResponse\x12\x46\n\x05watch\x12\x1c.replit.river.test.KVRequest\x1a\x1d.replit.river.test.KVResponse0\x01\x32Q\n\x06repeat\x12G\n\x04\x65\x63ho\x12\x1c.replit.river.test.EchoInput\x1a\x1d.replit.river.test.EchoOutput(\x01\x30\x01\x32S\n\x06upload\x12I\n\x04send\x12\x1e.replit.river.test.UploadInput\x1a\x1f.replit.river.test.UploadOutput(\x01\x62\x06proto3' +) # noqa: E501 + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages( + DESCRIPTOR, "testservice.protos.service_pb2", _globals +) # noqa: E501 +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals["_KVREQUEST"]._serialized_start = 55 + _globals["_KVREQUEST"]._serialized_end = 88 + _globals["_KVRESPONSE"]._serialized_start = 90 + _globals["_KVRESPONSE"]._serialized_end = 113 + _globals["_ECHOINPUT"]._serialized_start = 115 + _globals["_ECHOINPUT"]._serialized_end = 139 + _globals["_ECHOOUTPUT"]._serialized_start = 141 + _globals["_ECHOOUTPUT"]._serialized_end = 166 + _globals["_UPLOADINPUT"]._serialized_start = 168 + _globals["_UPLOADINPUT"]._serialized_end = 195 + _globals["_UPLOADOUTPUT"]._serialized_start = 197 + _globals["_UPLOADOUTPUT"]._serialized_end = 224 + _globals["_KV"]._serialized_start = 227 + _globals["_KV"]._serialized_end = 371 + _globals["_REPEAT"]._serialized_start = 373 + _globals["_REPEAT"]._serialized_end = 454 + _globals["_UPLOAD"]._serialized_start = 456 + _globals["_UPLOAD"]._serialized_end = 539 +# @@protoc_insertion_point(module_scope) diff --git a/impls/python-protocolv2/src/testservice/protos/service_pb2.pyi b/impls/python-protocolv2/src/testservice/protos/service_pb2.pyi new file mode 100644 index 0000000..823f333 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/service_pb2.pyi @@ -0,0 +1,105 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +import "google/protobuf/timestamp.proto";""" + +import builtins +import typing + +import google.protobuf.descriptor +import google.protobuf.message + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class KVRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + K_FIELD_NUMBER: builtins.int + V_FIELD_NUMBER: builtins.int + k: builtins.str + v: builtins.float + def __init__( + self, + *, + k: builtins.str = ..., + v: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["k", b"k", "v", b"v"]) -> None: ... + +global___KVRequest = KVRequest + +@typing.final +class KVResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + V_FIELD_NUMBER: builtins.int + v: builtins.float + def __init__( + self, + *, + v: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["v", b"v"]) -> None: ... + +global___KVResponse = KVResponse + +@typing.final +class EchoInput(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STR_FIELD_NUMBER: builtins.int + str: builtins.str + def __init__( + self, + *, + str: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["str", b"str"]) -> None: ... + +global___EchoInput = EchoInput + +@typing.final +class EchoOutput(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OUT_FIELD_NUMBER: builtins.int + out: builtins.str + def __init__( + self, + *, + out: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["out", b"out"]) -> None: ... + +global___EchoOutput = EchoOutput + +@typing.final +class UploadInput(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PART_FIELD_NUMBER: builtins.int + part: builtins.str + def __init__( + self, + *, + part: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["part", b"part"]) -> None: ... + +global___UploadInput = UploadInput + +@typing.final +class UploadOutput(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DOC_FIELD_NUMBER: builtins.int + doc: builtins.str + def __init__( + self, + *, + doc: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["doc", b"doc"]) -> None: ... + +global___UploadOutput = UploadOutput diff --git a/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.py b/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.py new file mode 100644 index 0000000..783bf4c --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.py @@ -0,0 +1,311 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" + +import grpc +import grpc.experimental # type: ignore + +from testservice.protos import service_pb2 as testservice_dot_protos_dot_service__pb2 + +GRPC_GENERATED_VERSION = "1.71.0" +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower # type: ignore + + _version_not_supported = first_version_is_lower( + GRPC_VERSION, GRPC_GENERATED_VERSION + ) # noqa: E501 +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f"The grpc package installed is at version {GRPC_VERSION}," + + " but the generated code in testservice/protos/service_pb2_grpc.py depends on" + + f" grpcio>={GRPC_GENERATED_VERSION}." + + f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}" + + f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}." + ) + + +class kvStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.set = channel.unary_unary( + "/replit.river.test.kv/set", + request_serializer=testservice_dot_protos_dot_service__pb2.KVRequest.SerializeToString, + response_deserializer=testservice_dot_protos_dot_service__pb2.KVResponse.FromString, + _registered_method=True, + ) + self.watch = channel.unary_stream( + "/replit.river.test.kv/watch", + request_serializer=testservice_dot_protos_dot_service__pb2.KVRequest.SerializeToString, + response_deserializer=testservice_dot_protos_dot_service__pb2.KVResponse.FromString, + _registered_method=True, + ) + + +class kvServicer(object): + """Missing associated documentation comment in .proto file.""" + + def set(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + def watch(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + +def add_kvServicer_to_server(servicer, server): + rpc_method_handlers = { + "set": grpc.unary_unary_rpc_method_handler( + servicer.set, + request_deserializer=testservice_dot_protos_dot_service__pb2.KVRequest.FromString, + response_serializer=testservice_dot_protos_dot_service__pb2.KVResponse.SerializeToString, + ), + "watch": grpc.unary_stream_rpc_method_handler( + servicer.watch, + request_deserializer=testservice_dot_protos_dot_service__pb2.KVRequest.FromString, + response_serializer=testservice_dot_protos_dot_service__pb2.KVResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + "replit.river.test.kv", rpc_method_handlers + ) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers("replit.river.test.kv", rpc_method_handlers) + + +# This class is part of an EXPERIMENTAL API. +class kv(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def set( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_unary( + request, + target, + "/replit.river.test.kv/set", + testservice_dot_protos_dot_service__pb2.KVRequest.SerializeToString, + testservice_dot_protos_dot_service__pb2.KVResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True, + ) + + @staticmethod + def watch( + request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.unary_stream( + request, + target, + "/replit.river.test.kv/watch", + testservice_dot_protos_dot_service__pb2.KVRequest.SerializeToString, + testservice_dot_protos_dot_service__pb2.KVResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True, + ) + + +class repeatStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.echo = channel.stream_stream( + "/replit.river.test.repeat/echo", + request_serializer=testservice_dot_protos_dot_service__pb2.EchoInput.SerializeToString, + response_deserializer=testservice_dot_protos_dot_service__pb2.EchoOutput.FromString, + _registered_method=True, + ) + + +class repeatServicer(object): + """Missing associated documentation comment in .proto file.""" + + def echo(self, request_iterator, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + +def add_repeatServicer_to_server(servicer, server): + rpc_method_handlers = { + "echo": grpc.stream_stream_rpc_method_handler( + servicer.echo, + request_deserializer=testservice_dot_protos_dot_service__pb2.EchoInput.FromString, + response_serializer=testservice_dot_protos_dot_service__pb2.EchoOutput.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + "replit.river.test.repeat", rpc_method_handlers + ) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers( + "replit.river.test.repeat", rpc_method_handlers + ) # noqa: E501 + + +# This class is part of an EXPERIMENTAL API. +class repeat(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def echo( + request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.stream_stream( + request_iterator, + target, + "/replit.river.test.repeat/echo", + testservice_dot_protos_dot_service__pb2.EchoInput.SerializeToString, + testservice_dot_protos_dot_service__pb2.EchoOutput.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True, + ) + + +class uploadStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.send = channel.stream_unary( + "/replit.river.test.upload/send", + request_serializer=testservice_dot_protos_dot_service__pb2.UploadInput.SerializeToString, + response_deserializer=testservice_dot_protos_dot_service__pb2.UploadOutput.FromString, + _registered_method=True, + ) + + +class uploadServicer(object): + """Missing associated documentation comment in .proto file.""" + + def send(self, request_iterator, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details("Method not implemented!") + raise NotImplementedError("Method not implemented!") + + +def add_uploadServicer_to_server(servicer, server): + rpc_method_handlers = { + "send": grpc.stream_unary_rpc_method_handler( + servicer.send, + request_deserializer=testservice_dot_protos_dot_service__pb2.UploadInput.FromString, + response_serializer=testservice_dot_protos_dot_service__pb2.UploadOutput.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + "replit.river.test.upload", rpc_method_handlers + ) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers( + "replit.river.test.upload", rpc_method_handlers + ) # noqa: E501 + + +# This class is part of an EXPERIMENTAL API. +class upload(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def send( + request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None, + ): + return grpc.experimental.stream_unary( + request_iterator, + target, + "/replit.river.test.upload/send", + testservice_dot_protos_dot_service__pb2.UploadInput.SerializeToString, + testservice_dot_protos_dot_service__pb2.UploadOutput.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True, + ) diff --git a/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.pyi b/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.pyi new file mode 100644 index 0000000..03c8580 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/service_pb2_grpc.pyi @@ -0,0 +1,108 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +import "google/protobuf/timestamp.proto";""" + +import abc +import collections.abc +import typing + +import grpc +import grpc.aio + +import testservice.protos.service_pb2 + +_T = typing.TypeVar("_T") + +# class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... # noqa: E501 +_MaybeAsyncIterator = collections.abc.AsyncIterator[_T] | collections.abc.Iterator[_T] + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] + ... + +class kvStub: + def __init__( + self, channel: typing.Union[grpc.Channel, grpc.aio.Channel] + ) -> None: ... # noqa: E501 + set: grpc.UnaryUnaryMultiCallable + + watch: grpc.UnaryStreamMultiCallable + +class kvAsyncStub: + set: grpc.aio.UnaryUnaryMultiCallable + + watch: grpc.aio.UnaryStreamMultiCallable + +class kvServicer(metaclass=abc.ABCMeta): + @abc.abstractmethod + def set( + self, + request: testservice.protos.service_pb2.KVRequest, + context: _ServicerContext, + ) -> typing.Union[ + testservice.protos.service_pb2.KVResponse, + collections.abc.Awaitable[testservice.protos.service_pb2.KVResponse], + ]: ... # noqa: E501 + @abc.abstractmethod + def watch( + self, + request: testservice.protos.service_pb2.KVRequest, + context: _ServicerContext, + ) -> typing.Union[ + collections.abc.Iterator[testservice.protos.service_pb2.KVResponse], + collections.abc.AsyncIterator[testservice.protos.service_pb2.KVResponse], + ]: ... # noqa: E501 + +def add_kvServicer_to_server( + servicer: kvServicer, server: typing.Union[grpc.Server, grpc.aio.Server] +) -> None: ... # noqa: E501 + +class repeatStub: + def __init__( + self, channel: typing.Union[grpc.Channel, grpc.aio.Channel] + ) -> None: ... # noqa: E501 + echo: grpc.StreamStreamMultiCallable + +class repeatAsyncStub: + echo: grpc.aio.StreamStreamMultiCallable + +class repeatServicer(metaclass=abc.ABCMeta): + @abc.abstractmethod + def echo( + self, + request_iterator: _MaybeAsyncIterator[testservice.protos.service_pb2.EchoInput], + context: _ServicerContext, + ) -> typing.Union[ + collections.abc.Iterator[testservice.protos.service_pb2.EchoOutput], + collections.abc.AsyncIterator[testservice.protos.service_pb2.EchoOutput], + ]: ... # noqa: E501 + +def add_repeatServicer_to_server( + servicer: repeatServicer, server: typing.Union[grpc.Server, grpc.aio.Server] +) -> None: ... # noqa: E501 + +class uploadStub: + def __init__( + self, channel: typing.Union[grpc.Channel, grpc.aio.Channel] + ) -> None: ... # noqa: E501 + send: grpc.StreamUnaryMultiCallable + +class uploadAsyncStub: + send: grpc.aio.StreamUnaryMultiCallable + +class uploadServicer(metaclass=abc.ABCMeta): + @abc.abstractmethod + def send( + self, + request_iterator: _MaybeAsyncIterator[ + testservice.protos.service_pb2.UploadInput + ], # noqa: E501 + context: _ServicerContext, + ) -> typing.Union[ + testservice.protos.service_pb2.UploadOutput, + collections.abc.Awaitable[testservice.protos.service_pb2.UploadOutput], + ]: ... # noqa: E501 + +def add_uploadServicer_to_server( + servicer: uploadServicer, server: typing.Union[grpc.Server, grpc.aio.Server] +) -> None: ... # noqa: E501 diff --git a/impls/python-protocolv2/src/testservice/protos/service_river.py b/impls/python-protocolv2/src/testservice/protos/service_river.py new file mode 100644 index 0000000..87b56e5 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/service_river.py @@ -0,0 +1,207 @@ +# ruff: noqa +# Code generated by river.codegen. DO NOT EDIT. +import datetime +from typing import Any, Mapping + +from google.protobuf import timestamp_pb2 +from google.protobuf.wrappers_pb2 import BoolValue + +import replit_river as river + +from testservice.protos import service_pb2, service_pb2_grpc + + +def _KVRequestEncoder(e: service_pb2.KVRequest) -> dict[str, Any]: + d: dict[str, Any] = {} + + _k = e.k + if _k is not None: + d["k"] = _k + _v = e.v + if _v is not None: + d["v"] = _v + return d + + +def _KVRequestDecoder( + d: Mapping[str, Any], +) -> service_pb2.KVRequest: + m = service_pb2.KVRequest() + if d is None: + return m + + if d.get("k") is not None: + setattr(m, "k", d["k"]) + if d.get("v") is not None: + setattr(m, "v", d["v"]) + return m + + +def _KVResponseEncoder(e: service_pb2.KVResponse) -> dict[str, Any]: + d: dict[str, Any] = {} + + _v = e.v + if _v is not None: + d["v"] = _v + return d + + +def _KVResponseDecoder( + d: Mapping[str, Any], +) -> service_pb2.KVResponse: + m = service_pb2.KVResponse() + if d is None: + return m + + if d.get("v") is not None: + setattr(m, "v", d["v"]) + return m + + +def _EchoInputEncoder(e: service_pb2.EchoInput) -> dict[str, Any]: + d: dict[str, Any] = {} + + _str = e.str + if _str is not None: + d["str"] = _str + return d + + +def _EchoInputDecoder( + d: Mapping[str, Any], +) -> service_pb2.EchoInput: + m = service_pb2.EchoInput() + if d is None: + return m + + if d.get("str") is not None: + setattr(m, "str", d["str"]) + return m + + +def _EchoOutputEncoder(e: service_pb2.EchoOutput) -> dict[str, Any]: + d: dict[str, Any] = {} + + _out = e.out + if _out is not None: + d["out"] = _out + return d + + +def _EchoOutputDecoder( + d: Mapping[str, Any], +) -> service_pb2.EchoOutput: + m = service_pb2.EchoOutput() + if d is None: + return m + + if d.get("out") is not None: + setattr(m, "out", d["out"]) + return m + + +def _UploadInputEncoder(e: service_pb2.UploadInput) -> dict[str, Any]: + d: dict[str, Any] = {} + + _part = e.part + if _part is not None: + d["part"] = _part + return d + + +def _UploadInputDecoder( + d: Mapping[str, Any], +) -> service_pb2.UploadInput: + m = service_pb2.UploadInput() + if d is None: + return m + + if d.get("part") is not None: + setattr(m, "part", d["part"]) + return m + + +def _UploadOutputEncoder(e: service_pb2.UploadOutput) -> dict[str, Any]: + d: dict[str, Any] = {} + + _doc = e.doc + if _doc is not None: + d["doc"] = _doc + return d + + +def _UploadOutputDecoder( + d: Mapping[str, Any], +) -> service_pb2.UploadOutput: + m = service_pb2.UploadOutput() + if d is None: + return m + + if d.get("doc") is not None: + setattr(m, "doc", d["doc"]) + return m + + +def add_kvServicer_to_server( + servicer: service_pb2_grpc.kvServicer, + server: river.Server, +) -> None: + rpc_method_handlers: Mapping[ + tuple[str, str], tuple[str, river.GenericRpcHandlerBuilder] + ] = { + ("kv", "set"): ( + "rpc", + river.rpc_method_handler( + servicer.set, # type: ignore + _KVRequestDecoder, + _KVResponseEncoder, + ), + ), + ("kv", "watch"): ( + "subscription-stream", + river.subscription_method_handler( + servicer.watch, # type: ignore + _KVRequestDecoder, + _KVResponseEncoder, + ), + ), + } + server.add_rpc_handlers(rpc_method_handlers) + + +def add_repeatServicer_to_server( + servicer: service_pb2_grpc.repeatServicer, + server: river.Server, +) -> None: + rpc_method_handlers: Mapping[ + tuple[str, str], tuple[str, river.GenericRpcHandlerBuilder] + ] = { + ("repeat", "echo"): ( + "stream", + river.stream_method_handler( + servicer.echo, # type: ignore + _EchoInputDecoder, + _EchoOutputEncoder, + ), + ), + } + server.add_rpc_handlers(rpc_method_handlers) + + +def add_uploadServicer_to_server( + servicer: service_pb2_grpc.uploadServicer, + server: river.Server, +) -> None: + rpc_method_handlers: Mapping[ + tuple[str, str], tuple[str, river.GenericRpcHandlerBuilder] + ] = { + ("upload", "send"): ( + "upload-stream", + river.upload_method_handler( + servicer.send, # type: ignore + _UploadInputDecoder, + _UploadOutputEncoder, + ), + ), + } + server.add_rpc_handlers(rpc_method_handlers) diff --git a/impls/python-protocolv2/src/testservice/protos/upload/__init__.py b/impls/python-protocolv2/src/testservice/protos/upload/__init__.py new file mode 100644 index 0000000..22483a1 --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/upload/__init__.py @@ -0,0 +1,46 @@ +# Code generated by river.codegen. DO NOT EDIT. +import datetime # noqa: F401 +from collections.abc import AsyncIterable, AsyncIterator # noqa: F401 +from typing import Any + +import replit_river as river +from pydantic import TypeAdapter # noqa: F401 +from replit_river.error_schema import RiverError, RiverErrorTypeAdapter # noqa: F401 + +from .send import ( + SendInit, + SendInitTypeAdapter, + SendInput, + SendInputTypeAdapter, + SendOutput, + SendOutputTypeAdapter, +) + + +class UploadService: + def __init__(self, client: river.v2.Client[Any]): + self.client = client + + async def send( + self, + init: SendInit, + inputStream: AsyncIterable[SendInput], + ) -> SendOutput: + return await self.client.send_upload( + "upload", + "send", + init, + inputStream, + lambda x: SendInitTypeAdapter.validate_python(x), + lambda x: SendInputTypeAdapter.dump_python( + x, # type: ignore[arg-type] + by_alias=True, + exclude_none=True, + ), + lambda x: SendOutputTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + lambda x: RiverErrorTypeAdapter.validate_python( + x # type: ignore[arg-type] + ), + ) diff --git a/impls/python-protocolv2/src/testservice/protos/upload/send.py b/impls/python-protocolv2/src/testservice/protos/upload/send.py new file mode 100644 index 0000000..d69ae3c --- /dev/null +++ b/impls/python-protocolv2/src/testservice/protos/upload/send.py @@ -0,0 +1,30 @@ +# Code generated by river.codegen. DO NOT EDIT. +from typing import ( + Literal, +) + +from pydantic import BaseModel, TypeAdapter + + +class SendInit(BaseModel): + pass + + +SendInputPart = Literal["EOF"] | str + + +class SendInput(BaseModel): + part: SendInputPart + + +class SendOutput(BaseModel): + doc: str + + +SendOutputTypeAdapter: TypeAdapter[SendOutput] = TypeAdapter(SendOutput) + + +SendInitTypeAdapter: TypeAdapter[SendInit] = TypeAdapter(SendInit) + + +SendInputTypeAdapter: TypeAdapter[SendInput] = TypeAdapter(SendInput) diff --git a/impls/python-protocolv2/uv.lock b/impls/python-protocolv2/uv.lock new file mode 100644 index 0000000..c0e212c --- /dev/null +++ b/impls/python-protocolv2/uv.lock @@ -0,0 +1,702 @@ +version = 1 +revision = 1 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] + +[[package]] +name = "aiochannel" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/a2/94d9fe4436bab8238120ff07b78043e9e4029fb1a102791c7a0b4475b6cb/aiochannel-1.3.0.tar.gz", hash = "sha256:212020426a9bddd29ad5039f16b9f86d8872bd8c22662aaf572189067d961b4f", size = 5323 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/d1/2a03c8cbd475688929d6ebc5a98a8490c818b962a102554320fcb047baae/aiochannel-1.3.0-py3-none-any.whl", hash = "sha256:8f862b0ff92a8296cc76876301c9f82348ee12fe5a8bcc198399b921f1eef944", size = 5507 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "coverage" +version = "7.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/4f/2251e65033ed2ce1e68f00f91a0294e0f80c80ae8c3ebbe2f12828c4cd53/coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", size = 811872 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/12/4792669473297f7973518bec373a955e267deb4339286f882439b8535b39/coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", size = 211684 }, + { url = "https://files.pythonhosted.org/packages/be/e1/2a4ec273894000ebedd789e8f2fc3813fcaf486074f87fd1c5b2cb1c0a2b/coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", size = 211935 }, + { url = "https://files.pythonhosted.org/packages/f8/3a/7b14f6e4372786709a361729164125f6b7caf4024ce02e596c4a69bccb89/coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", size = 245994 }, + { url = "https://files.pythonhosted.org/packages/54/80/039cc7f1f81dcbd01ea796d36d3797e60c106077e31fd1f526b85337d6a1/coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", size = 242885 }, + { url = "https://files.pythonhosted.org/packages/10/e0/dc8355f992b6cc2f9dcd5ef6242b62a3f73264893bc09fbb08bfcab18eb4/coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", size = 245142 }, + { url = "https://files.pythonhosted.org/packages/43/1b/33e313b22cf50f652becb94c6e7dae25d8f02e52e44db37a82de9ac357e8/coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", size = 244906 }, + { url = "https://files.pythonhosted.org/packages/05/08/c0a8048e942e7f918764ccc99503e2bccffba1c42568693ce6955860365e/coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", size = 243124 }, + { url = "https://files.pythonhosted.org/packages/5b/62/ea625b30623083c2aad645c9a6288ad9fc83d570f9adb913a2abdba562dd/coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", size = 244317 }, + { url = "https://files.pythonhosted.org/packages/62/cb/3871f13ee1130a6c8f020e2f71d9ed269e1e2124aa3374d2180ee451cee9/coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", size = 214170 }, + { url = "https://files.pythonhosted.org/packages/88/26/69fe1193ab0bfa1eb7a7c0149a066123611baba029ebb448500abd8143f9/coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", size = 214969 }, + { url = "https://files.pythonhosted.org/packages/f3/21/87e9b97b568e223f3438d93072479c2f36cc9b3f6b9f7094b9d50232acc0/coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", size = 211708 }, + { url = "https://files.pythonhosted.org/packages/75/be/882d08b28a0d19c9c4c2e8a1c6ebe1f79c9c839eb46d4fca3bd3b34562b9/coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", size = 211981 }, + { url = "https://files.pythonhosted.org/packages/7a/1d/ce99612ebd58082fbe3f8c66f6d8d5694976c76a0d474503fa70633ec77f/coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", size = 245495 }, + { url = "https://files.pythonhosted.org/packages/dc/8d/6115abe97df98db6b2bd76aae395fcc941d039a7acd25f741312ced9a78f/coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", size = 242538 }, + { url = "https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", size = 244561 }, + { url = "https://files.pythonhosted.org/packages/22/70/c10c77cd77970ac965734fe3419f2c98665f6e982744a9bfb0e749d298f4/coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", size = 244633 }, + { url = "https://files.pythonhosted.org/packages/38/5a/4f7569d946a07c952688debee18c2bb9ab24f88027e3d71fd25dbc2f9dca/coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", size = 242712 }, + { url = "https://files.pythonhosted.org/packages/bb/a1/03a43b33f50475a632a91ea8c127f7e35e53786dbe6781c25f19fd5a65f8/coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", size = 244000 }, + { url = "https://files.pythonhosted.org/packages/6a/89/ab6c43b1788a3128e4d1b7b54214548dcad75a621f9d277b14d16a80d8a1/coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", size = 214195 }, + { url = "https://files.pythonhosted.org/packages/12/12/6bf5f9a8b063d116bac536a7fb594fc35cb04981654cccb4bbfea5dcdfa0/coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", size = 214998 }, + { url = "https://files.pythonhosted.org/packages/2a/e6/1e9df74ef7a1c983a9c7443dac8aac37a46f1939ae3499424622e72a6f78/coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", size = 212541 }, + { url = "https://files.pythonhosted.org/packages/04/51/c32174edb7ee49744e2e81c4b1414ac9df3dacfcb5b5f273b7f285ad43f6/coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", size = 212767 }, + { url = "https://files.pythonhosted.org/packages/e9/8f/f454cbdb5212f13f29d4a7983db69169f1937e869a5142bce983ded52162/coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", size = 256997 }, + { url = "https://files.pythonhosted.org/packages/e6/74/2bf9e78b321216d6ee90a81e5c22f912fc428442c830c4077b4a071db66f/coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", size = 252708 }, + { url = "https://files.pythonhosted.org/packages/92/4d/50d7eb1e9a6062bee6e2f92e78b0998848a972e9afad349b6cdde6fa9e32/coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", size = 255046 }, + { url = "https://files.pythonhosted.org/packages/40/9e/71fb4e7402a07c4198ab44fc564d09d7d0ffca46a9fb7b0a7b929e7641bd/coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", size = 256139 }, + { url = "https://files.pythonhosted.org/packages/49/1a/78d37f7a42b5beff027e807c2843185961fdae7fe23aad5a4837c93f9d25/coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", size = 254307 }, + { url = "https://files.pythonhosted.org/packages/58/e9/8fb8e0ff6bef5e170ee19d59ca694f9001b2ec085dc99b4f65c128bb3f9a/coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", size = 255116 }, + { url = "https://files.pythonhosted.org/packages/56/b0/d968ecdbe6fe0a863de7169bbe9e8a476868959f3af24981f6a10d2b6924/coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", size = 214909 }, + { url = "https://files.pythonhosted.org/packages/87/e9/d6b7ef9fecf42dfb418d93544af47c940aa83056c49e6021a564aafbc91f/coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", size = 216068 }, + { url = "https://files.pythonhosted.org/packages/59/f1/4da7717f0063a222db253e7121bd6a56f6fb1ba439dcc36659088793347c/coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", size = 203435 }, +] + +[[package]] +name = "deprecated" +version = "1.2.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998 }, +] + +[[package]] +name = "deptry" +version = "0.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "packaging" }, + { name = "requirements-parser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/7e/75a1990a7244a3d3c5364353ac76f1173aa568a67793199d09f995b66c29/deptry-0.23.0.tar.gz", hash = "sha256:4915a3590ccf38ad7a9176aee376745aa9de121f50f8da8fb9ccec87fa93e676", size = 200920 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/85/a8b77c8a87e7c9e81ce8437d752879b5281fd8a0b8a114c6d393f980aa72/deptry-0.23.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1f2a6817a37d76e8f6b667381b7caf6ea3e6d6c18b5be24d36c625f387c79852", size = 1756706 }, + { url = "https://files.pythonhosted.org/packages/53/bf/26c58af1467df6e889c6b969c27dad2c67b8bd625320d9db7d70277a222f/deptry-0.23.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:9601b64cc0aed42687fdd5c912d5f1e90d7f7333fb589b14e35bfdfebae866f3", size = 1657001 }, + { url = "https://files.pythonhosted.org/packages/ae/7d/b0bd6a50ec3f87b0a5ed3bff64ac2bd5bd8d3205e570bc5bc3170f26a01f/deptry-0.23.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6172b2205f6e84bcc9df25226693d4deb9576a6f746c2ace828f6d13401d357", size = 1754607 }, + { url = "https://files.pythonhosted.org/packages/e6/1b/79b1213bb9b58b0bcc200867cd6d64cd76ec4b9c5cdb76f95c3e6ee7b92e/deptry-0.23.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cfa4b3a46ee8a026eaa38e4b9ba43fe6036a07fe16bf0a663cb611b939f6af8", size = 1831961 }, + { url = "https://files.pythonhosted.org/packages/09/d6/607004f20637987d437f420f3dad4d6f1a87a4a83380ab60220397ee8fbe/deptry-0.23.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9d03cc99a61c348df92074a50e0a71b28f264f0edbf686084ca90e6fd44e3abe", size = 1932126 }, + { url = "https://files.pythonhosted.org/packages/ff/ff/6fff20bf2632727af55dc3a24a6f5634dcdf34fd785402a55207ba49d9cc/deptry-0.23.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9a46f78098f145100dc582a59af8548b26cdfa16cf0fbd85d2d44645e724cb6a", size = 2004755 }, + { url = "https://files.pythonhosted.org/packages/41/30/1b6217bdccf2144d4c3e78f89b2a84db82478b2449599c2d3b4b21a89043/deptry-0.23.0-cp39-abi3-win_amd64.whl", hash = "sha256:d53e803b280791d89a051b6183d9dc40411200e22a8ab7e6c32c6b169822a664", size = 1606944 }, + { url = "https://files.pythonhosted.org/packages/28/ab/47398041d11b19aa9db28f28cf076dbe42aba3e16d67d3e7911330e3a304/deptry-0.23.0-cp39-abi3-win_arm64.whl", hash = "sha256:da7678624f4626d839c8c03675452cefc59d6cf57d25c84a9711dae514719279", size = 1518394 }, +] + +[[package]] +name = "grpcio" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/95/aa11fc09a85d91fbc7dd405dcb2a1e0256989d67bf89fa65ae24b3ba105a/grpcio-1.71.0.tar.gz", hash = "sha256:2b85f7820475ad3edec209d3d89a7909ada16caab05d3f2e08a7e8ae3200a55c", size = 12549828 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/83/bd4b6a9ba07825bd19c711d8b25874cd5de72c2a3fbf635c3c344ae65bd2/grpcio-1.71.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:0ff35c8d807c1c7531d3002be03221ff9ae15712b53ab46e2a0b4bb271f38537", size = 5184101 }, + { url = "https://files.pythonhosted.org/packages/31/ea/2e0d90c0853568bf714693447f5c73272ea95ee8dad107807fde740e595d/grpcio-1.71.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:b78a99cd1ece4be92ab7c07765a0b038194ded2e0a26fd654591ee136088d8d7", size = 11310927 }, + { url = "https://files.pythonhosted.org/packages/ac/bc/07a3fd8af80467390af491d7dc66882db43884128cdb3cc8524915e0023c/grpcio-1.71.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc1a1231ed23caac1de9f943d031f1bc38d0f69d2a3b243ea0d664fc1fbd7fec", size = 5654280 }, + { url = "https://files.pythonhosted.org/packages/16/af/21f22ea3eed3d0538b6ef7889fce1878a8ba4164497f9e07385733391e2b/grpcio-1.71.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6beeea5566092c5e3c4896c6d1d307fb46b1d4bdf3e70c8340b190a69198594", size = 6312051 }, + { url = "https://files.pythonhosted.org/packages/49/9d/e12ddc726dc8bd1aa6cba67c85ce42a12ba5b9dd75d5042214a59ccf28ce/grpcio-1.71.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5170929109450a2c031cfe87d6716f2fae39695ad5335d9106ae88cc32dc84c", size = 5910666 }, + { url = "https://files.pythonhosted.org/packages/d9/e9/38713d6d67aedef738b815763c25f092e0454dc58e77b1d2a51c9d5b3325/grpcio-1.71.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5b08d03ace7aca7b2fadd4baf291139b4a5f058805a8327bfe9aece7253b6d67", size = 6012019 }, + { url = "https://files.pythonhosted.org/packages/80/da/4813cd7adbae6467724fa46c952d7aeac5e82e550b1c62ed2aeb78d444ae/grpcio-1.71.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f903017db76bf9cc2b2d8bdd37bf04b505bbccad6be8a81e1542206875d0e9db", size = 6637043 }, + { url = "https://files.pythonhosted.org/packages/52/ca/c0d767082e39dccb7985c73ab4cf1d23ce8613387149e9978c70c3bf3b07/grpcio-1.71.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:469f42a0b410883185eab4689060a20488a1a0a00f8bbb3cbc1061197b4c5a79", size = 6186143 }, + { url = "https://files.pythonhosted.org/packages/00/61/7b2c8ec13303f8fe36832c13d91ad4d4ba57204b1c723ada709c346b2271/grpcio-1.71.0-cp312-cp312-win32.whl", hash = "sha256:ad9f30838550695b5eb302add33f21f7301b882937460dd24f24b3cc5a95067a", size = 3604083 }, + { url = "https://files.pythonhosted.org/packages/fd/7c/1e429c5fb26122055d10ff9a1d754790fb067d83c633ff69eddcf8e3614b/grpcio-1.71.0-cp312-cp312-win_amd64.whl", hash = "sha256:652350609332de6dac4ece254e5d7e1ff834e203d6afb769601f286886f6f3a8", size = 4272191 }, + { url = "https://files.pythonhosted.org/packages/04/dd/b00cbb45400d06b26126dcfdbdb34bb6c4f28c3ebbd7aea8228679103ef6/grpcio-1.71.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:cebc1b34ba40a312ab480ccdb396ff3c529377a2fce72c45a741f7215bfe8379", size = 5184138 }, + { url = "https://files.pythonhosted.org/packages/ed/0a/4651215983d590ef53aac40ba0e29dda941a02b097892c44fa3357e706e5/grpcio-1.71.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:85da336e3649a3d2171e82f696b5cad2c6231fdd5bad52616476235681bee5b3", size = 11310747 }, + { url = "https://files.pythonhosted.org/packages/57/a3/149615b247f321e13f60aa512d3509d4215173bdb982c9098d78484de216/grpcio-1.71.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f9a412f55bb6e8f3bb000e020dbc1e709627dcb3a56f6431fa7076b4c1aab0db", size = 5653991 }, + { url = "https://files.pythonhosted.org/packages/ca/56/29432a3e8d951b5e4e520a40cd93bebaa824a14033ea8e65b0ece1da6167/grpcio-1.71.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47be9584729534660416f6d2a3108aaeac1122f6b5bdbf9fd823e11fe6fbaa29", size = 6312781 }, + { url = "https://files.pythonhosted.org/packages/a3/f8/286e81a62964ceb6ac10b10925261d4871a762d2a763fbf354115f9afc98/grpcio-1.71.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9c80ac6091c916db81131d50926a93ab162a7e97e4428ffc186b6e80d6dda4", size = 5910479 }, + { url = "https://files.pythonhosted.org/packages/35/67/d1febb49ec0f599b9e6d4d0d44c2d4afdbed9c3e80deb7587ec788fcf252/grpcio-1.71.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:789d5e2a3a15419374b7b45cd680b1e83bbc1e52b9086e49308e2c0b5bbae6e3", size = 6013262 }, + { url = "https://files.pythonhosted.org/packages/a1/04/f9ceda11755f0104a075ad7163fc0d96e2e3a9fe25ef38adfc74c5790daf/grpcio-1.71.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1be857615e26a86d7363e8a163fade914595c81fec962b3d514a4b1e8760467b", size = 6643356 }, + { url = "https://files.pythonhosted.org/packages/fb/ce/236dbc3dc77cf9a9242adcf1f62538734ad64727fabf39e1346ad4bd5c75/grpcio-1.71.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a76d39b5fafd79ed604c4be0a869ec3581a172a707e2a8d7a4858cb05a5a7637", size = 6186564 }, + { url = "https://files.pythonhosted.org/packages/10/fd/b3348fce9dd4280e221f513dd54024e765b21c348bc475516672da4218e9/grpcio-1.71.0-cp313-cp313-win32.whl", hash = "sha256:74258dce215cb1995083daa17b379a1a5a87d275387b7ffe137f1d5131e2cfbb", size = 3601890 }, + { url = "https://files.pythonhosted.org/packages/be/f8/db5d5f3fc7e296166286c2a397836b8b042f7ad1e11028d82b061701f0f7/grpcio-1.71.0-cp313-cp313-win_amd64.whl", hash = "sha256:22c3bc8d488c039a199f7a003a38cb7635db6656fa96437a8accde8322ce2366", size = 4273308 }, +] + +[[package]] +name = "grpcio-tools" +version = "1.71.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio" }, + { name = "protobuf" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/d2/c0866a48c355a6a4daa1f7e27e210c7fa561b1f3b7c0bce2671e89cfa31e/grpcio_tools-1.71.0.tar.gz", hash = "sha256:38dba8e0d5e0fb23a034e09644fdc6ed862be2371887eee54901999e8f6792a8", size = 5326008 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e4/156956b92ad0298290c3d68e6670bc5a6fbefcccfe1ec3997480605e7135/grpcio_tools-1.71.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:61c0409d5bdac57a7bd0ce0ab01c1c916728fe4c8a03d77a25135ad481eb505c", size = 2385480 }, + { url = "https://files.pythonhosted.org/packages/c1/08/9930eb4bb38c5214041c9f24f8b35e9864a7938282db986836546c782d52/grpcio_tools-1.71.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:28784f39921d061d2164a9dcda5164a69d07bf29f91f0ea50b505958292312c9", size = 5951891 }, + { url = "https://files.pythonhosted.org/packages/73/65/931f29ec9c33719d48e1e30446ecce6f5d2cd4e4934fa73fbe07de41c43b/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:192808cf553cedca73f0479cc61d5684ad61f24db7a5f3c4dfe1500342425866", size = 2351967 }, + { url = "https://files.pythonhosted.org/packages/b8/26/2ec8748534406214f20a4809c36efcfa88d1a26246e8312102e3ef8c295d/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:989ee9da61098230d3d4c8f8f8e27c2de796f1ff21b1c90110e636d9acd9432b", size = 2745003 }, + { url = "https://files.pythonhosted.org/packages/f1/33/87b4610c86a4e10ee446b543a4d536f94ab04f828bab841f0bc1a083de72/grpcio_tools-1.71.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:541a756276c8a55dec991f6c0106ae20c8c8f5ce8d0bdbfcb01e2338d1a8192b", size = 2476455 }, + { url = "https://files.pythonhosted.org/packages/00/7c/f7f0cc36a43be9d45b3ce2a55245f3c7d063a24b7930dd719929e58871a4/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:870c0097700d13c403e5517cb7750ab5b4a791ce3e71791c411a38c5468b64bd", size = 2854333 }, + { url = "https://files.pythonhosted.org/packages/07/c4/34b9ea62b173c13fa7accba5f219355b320c05c80c79c3ba70fe52f47b2f/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:abd57f615e88bf93c3c6fd31f923106e3beb12f8cd2df95b0d256fa07a7a0a57", size = 3304297 }, + { url = "https://files.pythonhosted.org/packages/5c/ef/9d3449db8a07688dc3de7dcbd2a07048a128610b1a491c5c0cb3e90a00c5/grpcio_tools-1.71.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:753270e2d06d37e6d7af8967d1d059ec635ad215882041a36294f4e2fd502b2e", size = 2916212 }, + { url = "https://files.pythonhosted.org/packages/2e/c6/990e8194c934dfe7cf89ef307c319fa4f2bc0b78aeca707addbfa1e502f1/grpcio_tools-1.71.0-cp312-cp312-win32.whl", hash = "sha256:0e647794bd7138b8c215e86277a9711a95cf6a03ff6f9e555d54fdf7378b9f9d", size = 948849 }, + { url = "https://files.pythonhosted.org/packages/42/95/3c36d3205e6bd19853cc2420e44b6ef302eb4cfcf56498973c7e85f6c03b/grpcio_tools-1.71.0-cp312-cp312-win_amd64.whl", hash = "sha256:48debc879570972d28bfe98e4970eff25bb26da3f383e0e49829b2d2cd35ad87", size = 1120294 }, + { url = "https://files.pythonhosted.org/packages/84/a7/70dc7e9957bcbaccd4dcb6cc11215e0b918f546d55599221522fe0d073e0/grpcio_tools-1.71.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:9a78d07d6c301a25ef5ede962920a522556a1dfee1ccc05795994ceb867f766c", size = 2384758 }, + { url = "https://files.pythonhosted.org/packages/65/79/57320b28d0a0c5ec94095fd571a65292f8ed7e1c47e59ae4021e8a48d49b/grpcio_tools-1.71.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:580ac88141c9815557e63c9c04f5b1cdb19b4db8d0cb792b573354bde1ee8b12", size = 5951661 }, + { url = "https://files.pythonhosted.org/packages/80/3d/343df5ed7c5dd66fc7a19e4ef3e97ccc4f5d802122b04cd6492f0dcd79f5/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f7c678e68ece0ae908ecae1c4314a0c2c7f83e26e281738b9609860cc2c82d96", size = 2351571 }, + { url = "https://files.pythonhosted.org/packages/56/2f/b9736e8c84e880c4237f5b880c6c799b4977c5cde190999bc7ab4b2ec445/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56ecd6cc89b5e5eed1de5eb9cafce86c9c9043ee3840888cc464d16200290b53", size = 2744580 }, + { url = "https://files.pythonhosted.org/packages/76/9b/bdb384967353da7bf64bac4232f4cf8ae43f19d0f2f640978d4d4197e667/grpcio_tools-1.71.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a041afc20ab2431d756b6295d727bd7adee813b21b06a3483f4a7a15ea15f", size = 2475978 }, + { url = "https://files.pythonhosted.org/packages/26/71/1411487fd7862d347b98fda5e3beef611a71b2ac2faac62a965d9e2536b3/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a1712f12102b60c8d92779b89d0504e0d6f3a59f2b933e5622b8583f5c02992", size = 2853314 }, + { url = "https://files.pythonhosted.org/packages/03/06/59d0523eb1ba2f64edc72cb150152fa1b2e77061cae3ef3ecd3ef2a87f51/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:41878cb7a75477e62fdd45e7e9155b3af1b7a5332844021e2511deaf99ac9e6c", size = 3303981 }, + { url = "https://files.pythonhosted.org/packages/c2/71/fb9fb49f2b738ec1dfbbc8cdce0b26e5f9c5fc0edef72e453580620d6a36/grpcio_tools-1.71.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:682e958b476049ccc14c71bedf3f979bced01f6e0c04852efc5887841a32ad6b", size = 2915876 }, + { url = "https://files.pythonhosted.org/packages/bd/0f/0d49f6fe6fa2d09e9820dd9eeb30437e86002303076be2b6ada0fb52b8f2/grpcio_tools-1.71.0-cp313-cp313-win32.whl", hash = "sha256:0ccfb837152b7b858b9f26bb110b3ae8c46675d56130f6c2f03605c4f129be13", size = 948245 }, + { url = "https://files.pythonhosted.org/packages/bb/14/ab131a39187bfea950280b2277a82d2033469fe8c86f73b10b19f53cc5ca/grpcio_tools-1.71.0-cp313-cp313-win_amd64.whl", hash = "sha256:ffff9bc5eacb34dd26b487194f7d44a3e64e752fc2cf049d798021bf25053b87", size = 1119649 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "msgpack" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, + { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, + { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, + { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, + { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, + { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, + { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, + { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, + { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, + { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, + { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, +] + +[[package]] +name = "mypy" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "mypy-protobuf" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/6f/282d64d66bf48ce60e38a6560753f784e0f88ab245ac2fb5e93f701a36cd/mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c", size = 24445 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/73/d6b999782ae22f16971cc05378b3b33f6a89ede3b9619e8366aa23484bca/mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c", size = 16434 }, +] + +[[package]] +name = "nanoid" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/0250bf5935d88e214df469d35eccc0f6ff7e9db046fc8a9aeb4b2a192775/nanoid-2.0.0.tar.gz", hash = "sha256:5a80cad5e9c6e9ae3a41fa2fb34ae189f7cb420b2a5d8f82bd9d23466e4efa68", size = 3290 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/0d/8630f13998638dc01e187fadd2e5c6d42d127d08aeb4943d231664d6e539/nanoid-2.0.0-py3-none-any.whl", hash = "sha256:90aefa650e328cffb0893bbd4c236cfd44c48bc1f2d0b525ecc53c3187b653bb", size = 5844 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.31.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "importlib-metadata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/cf/db26ab9d748bf50d6edf524fb863aa4da616ba1ce46c57a7dff1112b73fb/opentelemetry_api-1.31.1.tar.gz", hash = "sha256:137ad4b64215f02b3000a0292e077641c8611aab636414632a9b9068593b7e91", size = 64059 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/c8/86557ff0da32f3817bc4face57ea35cfdc2f9d3bcefd42311ef860dcefb7/opentelemetry_api-1.31.1-py3-none-any.whl", hash = "sha256:1511a3f470c9c8a32eeea68d4ea37835880c0eed09dd1a0187acc8b1301da0a1", size = 65197 }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.31.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/d9/4fe159908a63661e9e635e66edc0d0d816ed20cebcce886132b19ae87761/opentelemetry_sdk-1.31.1.tar.gz", hash = "sha256:c95f61e74b60769f8ff01ec6ffd3d29684743404603df34b20aa16a49dc8d903", size = 159523 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/36/758e5d3746bc86a2af20aa5e2236a7c5aa4264b501dc0e9f40efd9078ef0/opentelemetry_sdk-1.31.1-py3-none-any.whl", hash = "sha256:882d021321f223e37afaca7b4e06c1d8bbc013f9e17ff48a7aa017460a8e7dae", size = 118866 }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.52b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/8c/599f9f27cff097ec4d76fbe9fe6d1a74577ceec52efe1a999511e3c42ef5/opentelemetry_semantic_conventions-0.52b1.tar.gz", hash = "sha256:7b3d226ecf7523c27499758a58b542b48a0ac8d12be03c0488ff8ec60c5bae5d", size = 111275 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/be/d4ba300cfc1d4980886efbc9b48ee75242b9fcf940d9c4ccdc9ef413a7cf/opentelemetry_semantic_conventions-0.52b1-py3-none-any.whl", hash = "sha256:72b42db327e29ca8bb1b91e8082514ddf3bbf33f32ec088feb09526ade4bc77e", size = 183409 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "protobuf" +version = "5.29.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/17/7d/b9dca7365f0e2c4fa7c193ff795427cfa6290147e5185ab11ece280a18e7/protobuf-5.29.4.tar.gz", hash = "sha256:4f1dfcd7997b31ef8f53ec82781ff434a28bf71d9102ddde14d076adcfc78c99", size = 424902 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/b2/043a1a1a20edd134563699b0e91862726a0dc9146c090743b6c44d798e75/protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7", size = 422709 }, + { url = "https://files.pythonhosted.org/packages/79/fc/2474b59570daa818de6124c0a15741ee3e5d6302e9d6ce0bdfd12e98119f/protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d", size = 434506 }, + { url = "https://files.pythonhosted.org/packages/46/de/7c126bbb06aa0f8a7b38aaf8bd746c514d70e6a2a3f6dd460b3b7aad7aae/protobuf-5.29.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:307ecba1d852ec237e9ba668e087326a67564ef83e45a0189a772ede9e854dd0", size = 417826 }, + { url = "https://files.pythonhosted.org/packages/a2/b5/bade14ae31ba871a139aa45e7a8183d869efe87c34a4850c87b936963261/protobuf-5.29.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:aec4962f9ea93c431d5714ed1be1c93f13e1a8618e70035ba2b0564d9e633f2e", size = 319574 }, + { url = "https://files.pythonhosted.org/packages/46/88/b01ed2291aae68b708f7d334288ad5fb3e7aa769a9c309c91a0d55cb91b0/protobuf-5.29.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:d7d3f7d1d5a66ed4942d4fefb12ac4b14a29028b209d4bfb25c68ae172059922", size = 319672 }, + { url = "https://files.pythonhosted.org/packages/12/fb/a586e0c973c95502e054ac5f81f88394f24ccc7982dac19c515acd9e2c93/protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862", size = 172551 }, +] + +[[package]] +name = "pydantic" +version = "2.9.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/b7/d9e3f12af310e1120c21603644a1cd86f59060e040ec5c3a80b8f05fae30/pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f", size = 769917 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/e4/ba44652d562cbf0bf320e0f3810206149c8a4e99cdbf66da82e97ab53a15/pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12", size = 434928 }, +] + +[[package]] +name = "pydantic-core" +version = "2.23.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/7b/8e315f80666194b354966ec84b7d567da77ad927ed6323db4006cf915f3f/pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231", size = 1856459 }, + { url = "https://files.pythonhosted.org/packages/14/de/866bdce10ed808323d437612aca1ec9971b981e1c52e5e42ad9b8e17a6f6/pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee", size = 1770007 }, + { url = "https://files.pythonhosted.org/packages/dc/69/8edd5c3cd48bb833a3f7ef9b81d7666ccddd3c9a635225214e044b6e8281/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87", size = 1790245 }, + { url = "https://files.pythonhosted.org/packages/80/33/9c24334e3af796ce80d2274940aae38dd4e5676298b4398eff103a79e02d/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8", size = 1801260 }, + { url = "https://files.pythonhosted.org/packages/a5/6f/e9567fd90104b79b101ca9d120219644d3314962caa7948dd8b965e9f83e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327", size = 1996872 }, + { url = "https://files.pythonhosted.org/packages/2d/ad/b5f0fe9e6cfee915dd144edbd10b6e9c9c9c9d7a56b69256d124b8ac682e/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2", size = 2661617 }, + { url = "https://files.pythonhosted.org/packages/06/c8/7d4b708f8d05a5cbfda3243aad468052c6e99de7d0937c9146c24d9f12e9/pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36", size = 2071831 }, + { url = "https://files.pythonhosted.org/packages/89/4d/3079d00c47f22c9a9a8220db088b309ad6e600a73d7a69473e3a8e5e3ea3/pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126", size = 1917453 }, + { url = "https://files.pythonhosted.org/packages/e9/88/9df5b7ce880a4703fcc2d76c8c2d8eb9f861f79d0c56f4b8f5f2607ccec8/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e", size = 1968793 }, + { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, + { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, + { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, + { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, + { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, + { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, + { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, + { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, + { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, + { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, + { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, + { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, + { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, + { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, +] + +[[package]] +name = "pyright" +version = "1.1.398" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/d6/48740f1d029e9fc4194880d1ad03dcf0ba3a8f802e0e166b8f63350b3584/pyright-1.1.398.tar.gz", hash = "sha256:357a13edd9be8082dc73be51190913e475fa41a6efb6ec0d4b7aab3bc11638d8", size = 3892675 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/e0/5283593f61b3c525d6d7e94cfb6b3ded20b3df66e953acaf7bb4f23b3f6e/pyright-1.1.398-py3-none-any.whl", hash = "sha256:0a70bfd007d9ea7de1cf9740e1ad1a40a122592cfe22a3f6791b06162ad08753", size = 5780235 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 }, +] + +[[package]] +name = "pytest-cov" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/69/5f1e57f6c5a39f81411b550027bf72842c4567ff5fd572bed1edc9e4b5d9/pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", size = 66857 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841 }, +] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, +] + +[[package]] +name = "replit-river" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiochannel" }, + { name = "grpcio" }, + { name = "grpcio-tools" }, + { name = "msgpack" }, + { name = "nanoid" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pydantic-core" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/10/2fb2c418e936bcef904c55d4d2fad62427f42a7ff30b108e9799b8f8a46c/replit_river-0.17.0.tar.gz", hash = "sha256:4da46d3e30484af8f2923f1362cb83cf28bb00207d36114d7f5e2d129a6be26d", size = 118227 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/3d/764973fbd779ffd9854187698eaae7960acc7f1c425b9c96c07b99a916ba/replit_river-0.17.0-py3-none-any.whl", hash = "sha256:0cddd6466bacf602c6f01016cf612aa11efc7f64255494b5fe3f4f7f62a9d193", size = 68838 }, +] + +[[package]] +name = "requirements-parser" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "types-setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/70/80ed53ebd21853855aad552d4ed6c4934df62cd32fe9a3669fcdef59429c/requirements_parser-0.11.0.tar.gz", hash = "sha256:35f36dc969d14830bf459803da84f314dc3d17c802592e9e970f63d0359e5920", size = 23663 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/33/190393a7d36872e237cbc99e6c44d9a078a1ba7b406462fe6eafd5a28e04/requirements_parser-0.11.0-py3-none-any.whl", hash = "sha256:50379eb50311834386c2568263ae5225d7b9d0867fb55cf4ecc93959de2c2684", size = 14800 }, +] + +[[package]] +name = "river-python-test" +version = "1.0.0" +source = { editable = "." } +dependencies = [ + { name = "replit-river" }, +] + +[package.dev-dependencies] +dev = [ + { name = "deptry" }, + { name = "mypy" }, + { name = "mypy-protobuf" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "ruff" }, + { name = "types-protobuf" }, +] + +[package.metadata] +requires-dist = [{ name = "replit-river", specifier = "==0.17.0" }] + +[package.metadata.requires-dev] +dev = [ + { name = "deptry", specifier = ">=0.23.0" }, + { name = "mypy", specifier = ">=1.15.0" }, + { name = "mypy-protobuf", specifier = ">=3.6.0" }, + { name = "pyright", specifier = ">=1.1.396" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-asyncio", specifier = ">=0.25.3" }, + { name = "pytest-cov", specifier = ">=4.1.0" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "ruff", specifier = ">=0.11.0" }, + { name = "types-protobuf", specifier = ">=5.29.1.20250315" }, +] + +[[package]] +name = "ruff" +version = "0.11.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/5b/3ae20f89777115944e89c2d8c2e795dcc5b9e04052f76d5347e35e0da66e/ruff-0.11.4.tar.gz", hash = "sha256:f45bd2fb1a56a5a85fae3b95add03fb185a0b30cf47f5edc92aa0355ca1d7407", size = 3933063 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/db/baee59ac88f57527fcbaad3a7b309994e42329c6bc4d4d2b681a3d7b5426/ruff-0.11.4-py3-none-linux_armv6l.whl", hash = "sha256:d9f4a761ecbde448a2d3e12fb398647c7f0bf526dbc354a643ec505965824ed2", size = 10106493 }, + { url = "https://files.pythonhosted.org/packages/c1/d6/9a0962cbb347f4ff98b33d699bf1193ff04ca93bed4b4222fd881b502154/ruff-0.11.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8c1747d903447d45ca3d40c794d1a56458c51e5cc1bc77b7b64bd2cf0b1626cc", size = 10876382 }, + { url = "https://files.pythonhosted.org/packages/3a/8f/62bab0c7d7e1ae3707b69b157701b41c1ccab8f83e8501734d12ea8a839f/ruff-0.11.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:51a6494209cacca79e121e9b244dc30d3414dac8cc5afb93f852173a2ecfc906", size = 10237050 }, + { url = "https://files.pythonhosted.org/packages/09/96/e296965ae9705af19c265d4d441958ed65c0c58fc4ec340c27cc9d2a1f5b/ruff-0.11.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f171605f65f4fc49c87f41b456e882cd0c89e4ac9d58e149a2b07930e1d466f", size = 10424984 }, + { url = "https://files.pythonhosted.org/packages/e5/56/644595eb57d855afed6e54b852e2df8cd5ca94c78043b2f29bdfb29882d5/ruff-0.11.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebf99ea9af918878e6ce42098981fc8c1db3850fef2f1ada69fb1dcdb0f8e79e", size = 9957438 }, + { url = "https://files.pythonhosted.org/packages/86/83/9d3f3bed0118aef3e871ded9e5687fb8c5776bde233427fd9ce0a45db2d4/ruff-0.11.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edad2eac42279df12e176564a23fc6f4aaeeb09abba840627780b1bb11a9d223", size = 11547282 }, + { url = "https://files.pythonhosted.org/packages/40/e6/0c6e4f5ae72fac5ccb44d72c0111f294a5c2c8cc5024afcb38e6bda5f4b3/ruff-0.11.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f103a848be9ff379fc19b5d656c1f911d0a0b4e3e0424f9532ececf319a4296e", size = 12182020 }, + { url = "https://files.pythonhosted.org/packages/b5/92/4aed0e460aeb1df5ea0c2fbe8d04f9725cccdb25d8da09a0d3f5b8764bf8/ruff-0.11.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:193e6fac6eb60cc97b9f728e953c21cc38a20077ed64f912e9d62b97487f3f2d", size = 11679154 }, + { url = "https://files.pythonhosted.org/packages/1b/d3/7316aa2609f2c592038e2543483eafbc62a0e1a6a6965178e284808c095c/ruff-0.11.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7af4e5f69b7c138be8dcffa5b4a061bf6ba6a3301f632a6bce25d45daff9bc99", size = 13905985 }, + { url = "https://files.pythonhosted.org/packages/63/80/734d3d17546e47ff99871f44ea7540ad2bbd7a480ed197fe8a1c8a261075/ruff-0.11.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:126b1bf13154aa18ae2d6c3c5efe144ec14b97c60844cfa6eb960c2a05188222", size = 11348343 }, + { url = "https://files.pythonhosted.org/packages/04/7b/70fc7f09a0161dce9613a4671d198f609e653d6f4ff9eee14d64c4c240fb/ruff-0.11.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8806daaf9dfa881a0ed603f8a0e364e4f11b6ed461b56cae2b1c0cab0645304", size = 10308487 }, + { url = "https://files.pythonhosted.org/packages/1a/22/1cdd62dabd678d75842bf4944fd889cf794dc9e58c18cc547f9eb28f95ed/ruff-0.11.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5d94bb1cc2fc94a769b0eb975344f1b1f3d294da1da9ddbb5a77665feb3a3019", size = 9929091 }, + { url = "https://files.pythonhosted.org/packages/9f/20/40e0563506332313148e783bbc1e4276d657962cc370657b2fff20e6e058/ruff-0.11.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:995071203d0fe2183fc7a268766fd7603afb9996785f086b0d76edee8755c896", size = 10924659 }, + { url = "https://files.pythonhosted.org/packages/b5/41/eef9b7aac8819d9e942f617f9db296f13d2c4576806d604aba8db5a753f1/ruff-0.11.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7a37ca937e307ea18156e775a6ac6e02f34b99e8c23fe63c1996185a4efe0751", size = 11428160 }, + { url = "https://files.pythonhosted.org/packages/ff/61/c488943414fb2b8754c02f3879de003e26efdd20f38167ded3fb3fc1cda3/ruff-0.11.4-py3-none-win32.whl", hash = "sha256:0e9365a7dff9b93af933dab8aebce53b72d8f815e131796268709890b4a83270", size = 10311496 }, + { url = "https://files.pythonhosted.org/packages/b6/2b/2a1c8deb5f5dfa3871eb7daa41492c4d2b2824a74d2b38e788617612a66d/ruff-0.11.4-py3-none-win_amd64.whl", hash = "sha256:5a9fa1c69c7815e39fcfb3646bbfd7f528fa8e2d4bebdcf4c2bd0fa037a255fb", size = 11399146 }, + { url = "https://files.pythonhosted.org/packages/4f/03/3aec4846226d54a37822e4c7ea39489e4abd6f88388fba74e3d4abe77300/ruff-0.11.4-py3-none-win_arm64.whl", hash = "sha256:d435db6b9b93d02934cf61ef332e66af82da6d8c69aefdea5994c89997c7a0fc", size = 10450306 }, +] + +[[package]] +name = "setuptools" +version = "78.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/5a/0db4da3bc908df06e5efae42b44e75c81dd52716e10192ff36d0c1c8e379/setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", size = 1367827 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/21/f43f0a1fa8b06b32812e0975981f4677d28e0f3271601dc88ac5a5b83220/setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8", size = 1256108 }, +] + +[[package]] +name = "types-protobuf" +version = "5.29.1.20250403" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/6d/62a2e73b966c77609560800004dd49a926920dd4976a9fdd86cf998e7048/types_protobuf-5.29.1.20250403.tar.gz", hash = "sha256:7ff44f15022119c9d7558ce16e78b2d485bf7040b4fadced4dd069bb5faf77a2", size = 59413 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e3/b74dcc2797b21b39d5a4f08a8b08e20369b4ca250d718df7af41a60dd9f0/types_protobuf-5.29.1.20250403-py3-none-any.whl", hash = "sha256:c71de04106a2d54e5b2173d0a422058fae0ef2d058d70cf369fb797bf61ffa59", size = 73874 }, +] + +[[package]] +name = "types-setuptools" +version = "78.1.0.20250329" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/6e/c54e6705e5fe67c3606e4c7c91123ecf10d7e1e6d7a9c11b52970cf2196c/types_setuptools-78.1.0.20250329.tar.gz", hash = "sha256:31e62950c38b8cc1c5114b077504e36426860a064287cac11b9666ab3a483234", size = 43942 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/31/85d0264705d8ef47680d28f4dc9bb1e27d8cace785fbe3f8d009fad6cb88/types_setuptools-78.1.0.20250329-py3-none-any.whl", hash = "sha256:ea47eab891afb506f470eee581dcde44d64dc99796665da794da6f83f50f6776", size = 66985 }, +] + +[[package]] +name = "typing-extensions" +version = "4.13.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 }, +] + +[[package]] +name = "websockets" +version = "13.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/46/c426282f543b3c0296cf964aa5a7bb17e984f58dde23460c3d39b3148fcf/websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", size = 157821 }, + { url = "https://files.pythonhosted.org/packages/aa/85/22529867010baac258da7c45848f9415e6cf37fef00a43856627806ffd04/websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", size = 155480 }, + { url = "https://files.pythonhosted.org/packages/29/2c/bdb339bfbde0119a6e84af43ebf6275278698a2241c2719afc0d8b0bdbf2/websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", size = 155715 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/8612029ea04c5c22bf7af2fd3d63876c4eaeef9b97e86c11972a43aa0e6c/websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", size = 165647 }, + { url = "https://files.pythonhosted.org/packages/56/04/1681ed516fa19ca9083f26d3f3a302257e0911ba75009533ed60fbb7b8d1/websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", size = 164592 }, + { url = "https://files.pythonhosted.org/packages/38/6f/a96417a49c0ed132bb6087e8e39a37db851c70974f5c724a4b2a70066996/websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", size = 165012 }, + { url = "https://files.pythonhosted.org/packages/40/8b/fccf294919a1b37d190e86042e1a907b8f66cff2b61e9befdbce03783e25/websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", size = 165311 }, + { url = "https://files.pythonhosted.org/packages/c1/61/f8615cf7ce5fe538476ab6b4defff52beb7262ff8a73d5ef386322d9761d/websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", size = 164692 }, + { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686 }, + { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712 }, + { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145 }, + { url = "https://files.pythonhosted.org/packages/51/20/2b99ca918e1cbd33c53db2cace5f0c0cd8296fc77558e1908799c712e1cd/websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6", size = 157828 }, + { url = "https://files.pythonhosted.org/packages/b8/47/0932a71d3d9c0e9483174f60713c84cee58d62839a143f21a2bcdbd2d205/websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708", size = 155487 }, + { url = "https://files.pythonhosted.org/packages/a9/60/f1711eb59ac7a6c5e98e5637fef5302f45b6f76a2c9d64fd83bbb341377a/websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418", size = 155721 }, + { url = "https://files.pythonhosted.org/packages/6a/e6/ba9a8db7f9d9b0e5f829cf626ff32677f39824968317223605a6b419d445/websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a", size = 165609 }, + { url = "https://files.pythonhosted.org/packages/c1/22/4ec80f1b9c27a0aebd84ccd857252eda8418ab9681eb571b37ca4c5e1305/websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f", size = 164556 }, + { url = "https://files.pythonhosted.org/packages/27/ac/35f423cb6bb15600438db80755609d27eda36d4c0b3c9d745ea12766c45e/websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5", size = 164993 }, + { url = "https://files.pythonhosted.org/packages/31/4e/98db4fd267f8be9e52e86b6ee4e9aa7c42b83452ea0ea0672f176224b977/websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135", size = 165360 }, + { url = "https://files.pythonhosted.org/packages/3f/15/3f0de7cda70ffc94b7e7024544072bc5b26e2c1eb36545291abb755d8cdb/websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2", size = 164745 }, + { url = "https://files.pythonhosted.org/packages/a1/6e/66b6b756aebbd680b934c8bdbb6dcb9ce45aad72cde5f8a7208dbb00dd36/websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", size = 164732 }, + { url = "https://files.pythonhosted.org/packages/35/c6/12e3aab52c11aeb289e3dbbc05929e7a9d90d7a9173958477d3ef4f8ce2d/websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", size = 158709 }, + { url = "https://files.pythonhosted.org/packages/41/d8/63d6194aae711d7263df4498200c690a9c39fb437ede10f3e157a6343e0d/websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", size = 159144 }, + { url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] diff --git a/impls/python/generate_client.sh b/impls/python/generate_client.sh index a380aba..ff07413 100755 --- a/impls/python/generate_client.sh +++ b/impls/python/generate_client.sh @@ -41,9 +41,6 @@ if ! uv run ruff check --fix; then fi uv run ruff format - -git add src/testservice/protos - uv run pyright . echo "Completed" diff --git a/impls/python/src/testservice/client.py b/impls/python/src/testservice/client.py index b13dacf..8112c4d 100644 --- a/impls/python/src/testservice/client.py +++ b/impls/python/src/testservice/client.py @@ -4,7 +4,7 @@ import os import sys from datetime import timedelta -from typing import AsyncIterator, Dict +from typing import Any, AsyncGenerator, AsyncIterator, Dict from replit_river import ( Client, @@ -43,7 +43,7 @@ logging.basicConfig( level=logging.DEBUG, - format="Python Server %(asctime)s - %(levelname)s - %(message)s", + format="Python Client %(asctime)s - %(levelname)s - %(message)s", ) @@ -51,7 +51,36 @@ tasks: Dict[str, asyncio.Task] = {} -async def process_commands() -> None: +async def asyncly_emit( + actions: list[Any], +) -> AsyncGenerator[tuple[str, Any], None]: # noqa: E501 + for action in actions: + line = json.dumps(action) + yield line, action + + +async def read_from_stdin() -> AsyncGenerator[tuple[str, Any], None]: + while True: + line = await asyncio.get_event_loop().run_in_executor(None, sys.stdin.readline) + + # We're done + if not line: + break + + try: + action = json.loads(line) + except json.JSONDecodeError as e: + # Sometimes docker injects this into the stream: + # {"hijack":true,"stream":true,"stdin":true,"stdout":true,"stderr":true}{"type": "invoke", ... # noqa: E501 + offset = e.colno - 1 + first = json.loads(line[0:offset]) + assert "hijack" in first + action = json.loads(line[offset:]) + + yield line, action + + +async def process_commands(static_actions: list[dict[Any, Any]] | None) -> None: logging.error("start python river client") uri = f"ws://{RIVER_SERVER}:{PORT}" logging.error( @@ -81,23 +110,12 @@ async def get_connection_metadata() -> UriAndMetadata[None]: ) test_client = TestCient(client) try: - while True: - line = await asyncio.get_event_loop().run_in_executor( - None, sys.stdin.readline - ) - if not line: - break - - try: - action = json.loads(line) - except json.JSONDecodeError as e: - # Sometimes docker injects this into the stream: - # {"hijack":true,"stream":true,"stdin":true,"stdout":true,"stderr":true}{"type": "invoke", ... # noqa: E501 - offset = e.colno - 1 - first = json.loads(line[0:offset]) - assert "hijack" in first - action = json.loads(line[offset:]) + if static_actions: + actions = asyncly_emit(static_actions) + else: + actions = read_from_stdin() + async for line, action in actions: if not action: print("FATAL: invalid command", line) sys.exit(1) @@ -153,6 +171,8 @@ async def get_connection_metadata() -> UriAndMetadata[None]: await client.close() for task in tasks.values(): task.cancel() + while not task.done(): + await asyncio.sleep(0.1) exception = task.exception() if exception is not None: logging.error("Task raised an exception: {}", exception) @@ -218,7 +238,13 @@ def print_result(result: EchoOutput | RiverError) -> None: async def main() -> None: - await process_commands() + # static_actions = [ + # {"type":"invoke","id":"1","proc":"repeat.echo","init":{}}, + # {"type":"invoke","id":"1","proc":"repeat.echo","payload":{"s":"hello"}}, + # {"type":"invoke","id":"1","proc":"repeat.echo","payload":{"s":"world"}} + # ] + # await process_commands(static_actions) + await process_commands(None) if __name__ == "__main__": diff --git a/index.ts b/index.ts index 92a5596..6fc404e 100644 --- a/index.ts +++ b/index.ts @@ -33,6 +33,7 @@ import { PRESET_TIMER, type ListrTask } from 'listr2'; import { Manager } from '@listr2/manager'; import { constants, open } from 'fs/promises'; import assert from 'assert'; +import path from 'path'; const { client: clientImpl, @@ -280,6 +281,7 @@ async function runSuite( const testsFailed = new Set(); const testsFlaked = new Set(); + const testsSkipped = new Set(); const logsDir = `./logs/${clientImpl}-${serverImpl}/${Date.now()}/`; await mkdir(logsDir, { recursive: true }); @@ -320,7 +322,7 @@ async function runSuite( log('status: writing results'); - const stderrLogFilePath = `${logsDir}/${name}.log`; + const stderrLogFilePath = path.join(logsDir, `${name}.log`); const logFileHandle = await open( stderrLogFilePath, constants.O_APPEND | constants.O_WRONLY | constants.O_CREAT, @@ -337,24 +339,30 @@ async function runSuite( test.unordered ?? false, ); - let diffMsg: string | undefined = undefined; + let diffMsg: string = ""; if (hasDiff) { - const failMessage = test.flaky - ? chalk.black.bgYellow(' FLAKED ') - : chalk.black.bgRed(' FAIL '); + let preamble: string; + let failMessage: string; + if ((test.unsupported?.indexOf(clientImpl) ?? -1) >= 0) { + failMessage = chalk.black.bgGreen(' Skipped '); + preamble = `clientName: ${chalk.green(clientName)} ${failMessage}`; + testsSkipped.add(name); + } else if (test.flaky) { + failMessage = chalk.black.bgYellow(' FLAKED '); + preamble = `clientName: ${chalk.red(clientName)} ${failMessage}`; + testsFlaked.add(name); + } else { + failMessage = chalk.black.bgRed(' FAIL ') + preamble = `clientName: ${chalk.red(clientName)} ${failMessage}`; + testsFailed.add(name); + } diffMsg = ` -clientName: ${chalk.red(clientName)} ${failMessage} +${preamble} diff: ${diff} `; - - if (test.flaky) { - testsFlaked.add(name); - } else { - testsFailed.add(name); - } } const logOutput = stripAnsi(` @@ -383,6 +391,8 @@ logs will be written to ${stderrLogFilePath} throw new Error('test failed'); } else if (testsFlaked.has(name)) { task.skip('flaked'); + } else if (testsSkipped.has(name)) { + task.skip('unsupported'); } }, }), @@ -425,6 +435,11 @@ ${chalk.red(`failed:`)} ${Array.from(testsFailed) .map((name) => chalk.red(`- ${name}\n`)) .join('\n')} + +${chalk.yellow(`skipped:`)} +${Array.from(testsSkipped) + .map((name) => chalk.yellow(`- ${name}\n`)) + .join('\n')} `; await writeFile(`${logsDir}/summary.txt`, stripAnsi(summary)); diff --git a/schema-v2.json b/schema-v2.json new file mode 100644 index 0000000..65265dd --- /dev/null +++ b/schema-v2.json @@ -0,0 +1,200 @@ +{ + "services": { + "kv": { + "procedures": { + "set": { + "init": { + "type": "object", + "properties": { + "k": { + "type": "string" + }, + "v": { + "type": "number" + } + }, + "required": [ + "k", + "v" + ] + }, + "output": { + "type": "object", + "properties": { + "v": { + "type": "integer" + } + }, + "required": [ + "v" + ] + }, + "errors": { + "not": {} + }, + "type": "rpc" + }, + "watch": { + "init": { + "type": "object", + "properties": { + "k": { + "type": "string" + } + }, + "required": [ + "k" + ] + }, + "output": { + "type": "object", + "properties": { + "v": { + "type": "number" + } + }, + "required": [ + "v" + ] + }, + "errors": { + "type": "object", + "properties": { + "code": { + "const": "NOT_FOUND", + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ] + }, + "type": "subscription" + } + } + }, + "repeat": { + "procedures": { + "echo": { + "init": { + "type": "object", + "properties": {} + }, + "input": { + "type": "object", + "properties": { + "str": { + "type": "string" + } + }, + "required": [ + "str" + ] + }, + "output": { + "type": "object", + "properties": { + "out": { + "type": "string" + } + }, + "required": [ + "out" + ] + }, + "errors": { + "not": {} + }, + "type": "stream" + }, + "echo_prefix": { + "init": { + "type": "object", + "properties": { + "prefix": { + "type": "string" + } + }, + "required": [ + "prefix" + ] + }, + "input": { + "type": "object", + "properties": { + "str": { + "type": "string" + } + }, + "required": [ + "str" + ] + }, + "output": { + "type": "object", + "properties": { + "out": { + "type": "string" + } + }, + "required": [ + "out" + ] + }, + "errors": { + "not": {} + }, + "type": "stream" + } + } + }, + "upload": { + "procedures": { + "send": { + "init": { + "type": "object", + "properties": {} + }, + "input": { + "type": "object", + "properties": { + "part": { + "anyOf": [ + { + "type": "string" + }, + { + "const": "EOF", + "type": "string" + } + ] + } + }, + "required": [ + "part" + ] + }, + "output": { + "type": "object", + "properties": { + "doc": { + "type": "string" + } + }, + "required": [ + "doc" + ] + }, + "errors": { + "not": {} + }, + "type": "upload" + } + } + } + } +} diff --git a/src/actions.ts b/src/actions.ts index 9c2b981..46c1b41 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -123,4 +123,5 @@ export interface Test { // Unordered means that prior to diffing the output, it will be sorted lexicographically. // Useful for tests that have inherent racy output. unordered?: boolean; + unsupported?: string[]; } diff --git a/tests/disconnect_notifs.ts b/tests/disconnect_notifs.ts index dd56ad2..859af1b 100644 --- a/tests/disconnect_notifs.ts +++ b/tests/disconnect_notifs.ts @@ -32,7 +32,7 @@ const RpcDisconnectNotifs: Test = { }; const SubscribeDisconnectNotifs: Test = { - flaky: true, + unsupported: ["node-protocolv2", "python-protocolv2"], clients: { client: { actions: [