From 1d0b323c6b309f44f0c97f43ece35ef09098fb59 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 9 Jul 2026 19:26:16 +0000 Subject: [PATCH] sdk: fix pyright test typing Co-authored-by: Lucas Parzianello --- sdk/src/spectrumx/utils.py | 9 ++++++++- sdk/tests/models/test_datasets.py | 2 ++ sdk/tests/models/test_user.py | 14 ++++++++------ sdk/tests/ops/test_captures.py | 3 ++- sdk/tests/ops/test_files.py | 2 ++ sdk/tests/test_gateway.py | 13 +++++++++---- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/sdk/src/spectrumx/utils.py b/sdk/src/spectrumx/utils.py index 85ed421cc..e368b0fe5 100644 --- a/sdk/src/spectrumx/utils.py +++ b/sdk/src/spectrumx/utils.py @@ -20,6 +20,7 @@ from typing import TYPE_CHECKING from typing import Any from typing import NoReturn +from typing import Protocol from typing import TypeVar from typing import cast @@ -36,6 +37,12 @@ logger = logging.getLogger(__name__) +class SupportsProgressUpdate(Protocol): + def update(self, n: int = 1) -> bool | None: + """Advance progress by ``n`` units.""" + ... + + def validate_file_permission_string(permissions: str) -> None: """Make sure a unix-like permissions string is valid.""" perm_flags_len = 9 @@ -186,7 +193,7 @@ def credit_unstreamed_file_bytes( *, file_size: int, bytes_streamed: int, - prog_bar: tqdm[NoReturn] | None, + prog_bar: SupportsProgressUpdate | None, bytes_accounted: list[int] | None = None, ) -> int: """Credit progress for file bytes that were not transferred over the wire. diff --git a/sdk/tests/models/test_datasets.py b/sdk/tests/models/test_datasets.py index 78907f155..e119aa635 100644 --- a/sdk/tests/models/test_datasets.py +++ b/sdk/tests/models/test_datasets.py @@ -180,9 +180,11 @@ def test_parses_raw_gateway_payload_with_nested_coercion(self) -> None: assert dataset.uuid == ds_uid assert isinstance(dataset.owner, User) assert dataset.owner.name == "Owner" + assert dataset.captures is not None assert isinstance(dataset.captures[0], DatasetCapture) assert dataset.captures[0].capture_type is CaptureType.DigitalRF assert isinstance(dataset.captures[0].owner, User) + assert dataset.files is not None assert isinstance(dataset.files[0], DatasetFile) def test_model_dump_round_trips(self) -> None: diff --git a/sdk/tests/models/test_user.py b/sdk/tests/models/test_user.py index 3cfb4a453..7c91e8536 100644 --- a/sdk/tests/models/test_user.py +++ b/sdk/tests/models/test_user.py @@ -79,7 +79,7 @@ def test_create_with_name_and_email(self) -> None: def test_create_with_defaults(self) -> None: """User created with no args has all fields as None.""" - user = User() + user = User.model_validate({}) assert user.name is None assert user.email is None assert user.uuid is None @@ -114,11 +114,13 @@ def test_valid_construction(self) -> None: def test_item_type_coerced_from_string(self) -> None: """item_type accepts string serialization value and coerces to enum.""" - perm = UserSharePermission( - item_type="capture", - item_uuid=uuid.uuid4(), - shared_with=self._shared_with(), - permission_level="co-owner", + perm = UserSharePermission.model_validate( + { + "item_type": "capture", + "item_uuid": uuid.uuid4(), + "shared_with": self._shared_with(), + "permission_level": "co-owner", + } ) assert perm.item_type is ItemType.CAPTURE assert perm.permission_level is PermissionLevel.CO_OWNER diff --git a/sdk/tests/ops/test_captures.py b/sdk/tests/ops/test_captures.py index 5ee3ad6c6..a92422a03 100644 --- a/sdk/tests/ops/test_captures.py +++ b/sdk/tests/ops/test_captures.py @@ -35,6 +35,7 @@ if TYPE_CHECKING: from collections.abc import Generator + from collections.abc import Mapping import responses from pydantic import UUID4 @@ -1732,7 +1733,7 @@ def test_extract_page_from_payload_dict_with_empty_next() -> None: class _GatewayStub: """Minimal stub that emulates the gateway list endpoint.""" - def __init__(self, payload: dict[str, object]) -> None: + def __init__(self, payload: Mapping[str, object]) -> None: self._payload = payload self.calls = 0 diff --git a/sdk/tests/ops/test_files.py b/sdk/tests/ops/test_files.py index 230b43ad7..3fc8d132d 100644 --- a/sdk/tests/ops/test_files.py +++ b/sdk/tests/ops/test_files.py @@ -1437,6 +1437,7 @@ def test_download_file_to_directory_path( client=client, file_uuid=file_id.hex, to_local_path=target_dir ) downloaded_path = result.local_path + assert downloaded_path is not None assert downloaded_path == expected_path assert downloaded_path.exists() assert downloaded_path.stat().st_size == num_bytes @@ -1767,6 +1768,7 @@ def test_upload_file_instance_without_directory(client: Client, tmp_path: Path) name=file_path.name, media_type="text/plain", size=file_path.stat().st_size, + directory=PurePosixPath(), permissions="rw-r--r--", created_at=datetime(2024, 12, 1, 12, 0, 0, tzinfo=UTC), updated_at=datetime(2024, 12, 1, 12, 0, 0, tzinfo=UTC), diff --git a/sdk/tests/test_gateway.py b/sdk/tests/test_gateway.py index e7ea36ce0..cc89afa09 100644 --- a/sdk/tests/test_gateway.py +++ b/sdk/tests/test_gateway.py @@ -10,6 +10,7 @@ import uuid from datetime import datetime from pathlib import Path +from pathlib import PurePosixPath from typing import Any from unittest.mock import patch from urllib.parse import parse_qs @@ -337,7 +338,7 @@ def test_update_existing_file_metadata_sends_put() -> None: name="test.txt", media_type="text/plain", size=100, - directory=Path("/some/dir"), + directory=PurePosixPath("/some/dir"), permissions="rw-r--r--", created_at=datetime(2024, 1, 1, tzinfo=UTC), updated_at=datetime(2024, 1, 1, tzinfo=UTC), @@ -361,7 +362,7 @@ def test_update_existing_file_metadata_requires_uuid() -> None: name="test.txt", media_type="text/plain", size=100, - directory=Path("/some/dir"), + directory=PurePosixPath("/some/dir"), permissions="rw-r--r--", created_at=datetime(2024, 1, 1, tzinfo=UTC), updated_at=datetime(2024, 1, 1, tzinfo=UTC), @@ -408,7 +409,7 @@ def test_upload_new_file_raises_file_error_when_no_local_path() -> None: name="test.txt", media_type="text/plain", size=100, - directory=Path("/some/dir"), + directory=PurePosixPath("/some/dir"), permissions="rw-r--r--", created_at=datetime(2024, 1, 1, tzinfo=UTC), updated_at=datetime(2024, 1, 1, tzinfo=UTC), @@ -446,7 +447,11 @@ def test_create_capture_with_channel_scan_group_name() -> None: # create_capture sends form-encoded data (requests `data=`), so decode the body # and assert structured fields — robust to key naming (avoids "groupA" matching # a "scan_group_name" key by accident, which a substring check would do). - body = parse_qs(responses.calls[0].request.body) + request_body = responses.calls[0].request.body + assert isinstance(request_body, str | bytes) + if isinstance(request_body, bytes): + request_body = request_body.decode() + body = parse_qs(request_body) assert body["channel"] == ["chan1"] assert body["scan_group"] == ["groupA"] assert body["name"] == ["my_capture"]