diff --git a/Makefile b/Makefile index 178db11b..495427b8 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,9 @@ help:: @echo " stop-infra -- Stop all infra used by tests." @echo " NOTE: run this command to ensure all containers are stopped after tests" +mypy: $(call FORALL_PKGS,mypy) +help:: + @echo " mypy -- Run mypy on all files" ruff: @uv run ruff check --fix diff --git a/flake.lock b/flake.lock index 9e22d517..44d697a9 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1756542300, - "narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=", + "lastModified": 1762363567, + "narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d7600c775f877cd87b4f5a831c28aa94137377aa", + "rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4", "type": "github" }, "original": { diff --git a/pyproject.toml b/pyproject.toml index ac60986e..ed64b96f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,3 +18,28 @@ supabase = { workspace = true } [tool.pytest.ini_options] asyncio_mode = "auto" + +[tool.ruff] +target-version = "py39" + +[tool.ruff.lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # flake8-bugbear + "B", + # flake8-simplify + # "SIM", + # isort + "I", + "ANN2" +] +ignore = ["F403", "E501", "E402", "UP006", "UP035"] + +[tool.ruff.lint.pyupgrade] +# Preserve types, even if a file imports `from __future__ import annotations`. +keep-runtime-typing = true diff --git a/src/auth/pyproject.toml b/src/auth/pyproject.toml index 690ed037..ad05f666 100644 --- a/src/auth/pyproject.toml +++ b/src/auth/pyproject.toml @@ -50,29 +50,6 @@ lints = [ ] dev = [{ include-group = "lints" }, {include-group = "tests" }] -[tool.ruff.lint] -select = [ - # pycodestyle - "E", - # Pyflakes - "F", - # pyupgrade - "UP", - # flake8-bugbear - "B", - # flake8-simplify - # "SIM", - # isort - "I", - "ANN2" -] -ignore = ["F403", "E501", "E402", "UP006", "UP035"] - -[tool.ruff.lint.pyupgrade] -# Preserve types, even if a file imports `from __future__ import annotations`. -keep-runtime-typing = true - - [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/src/auth/tests/_async/clients.py b/src/auth/tests/_async/clients.py index fda5360c..eb2686e5 100644 --- a/src/auth/tests/_async/clients.py +++ b/src/auth/tests/_async/clients.py @@ -5,10 +5,9 @@ from faker import Faker from jwt import encode -from typing_extensions import NotRequired, TypedDict - from supabase_auth import AsyncGoTrueAdminAPI, AsyncGoTrueClient from supabase_auth.types import User +from typing_extensions import NotRequired, TypedDict def mock_access_token() -> str: diff --git a/src/auth/tests/_async/test_gotrue.py b/src/auth/tests/_async/test_gotrue.py index 4258bf71..f20861d1 100644 --- a/src/auth/tests/_async/test_gotrue.py +++ b/src/auth/tests/_async/test_gotrue.py @@ -3,7 +3,6 @@ import pytest from jwt import encode - from supabase_auth.errors import ( AuthApiError, AuthInvalidJwtError, @@ -489,7 +488,6 @@ async def test_sign_in_with_otp() -> None: from unittest.mock import patch from httpx import Response - from supabase_auth.types import AuthOtpResponse # First test for email OTP diff --git a/src/auth/tests/_async/test_gotrue_admin_api.py b/src/auth/tests/_async/test_gotrue_admin_api.py index de48cfcf..02f8b95c 100644 --- a/src/auth/tests/_async/test_gotrue_admin_api.py +++ b/src/auth/tests/_async/test_gotrue_admin_api.py @@ -1,7 +1,6 @@ import uuid import pytest - from supabase_auth.errors import ( AuthApiError, AuthError, diff --git a/src/auth/tests/_sync/clients.py b/src/auth/tests/_sync/clients.py index 159b68d7..162e5f9a 100644 --- a/src/auth/tests/_sync/clients.py +++ b/src/auth/tests/_sync/clients.py @@ -5,10 +5,9 @@ from faker import Faker from jwt import encode -from typing_extensions import NotRequired, TypedDict - from supabase_auth import SyncGoTrueAdminAPI, SyncGoTrueClient from supabase_auth.types import User +from typing_extensions import NotRequired, TypedDict def mock_access_token() -> str: diff --git a/src/auth/tests/_sync/test_gotrue.py b/src/auth/tests/_sync/test_gotrue.py index 53241056..f260622d 100644 --- a/src/auth/tests/_sync/test_gotrue.py +++ b/src/auth/tests/_sync/test_gotrue.py @@ -3,7 +3,6 @@ import pytest from jwt import encode - from supabase_auth.errors import ( AuthApiError, AuthInvalidJwtError, @@ -487,7 +486,6 @@ def test_sign_in_with_otp() -> None: from unittest.mock import patch from httpx import Response - from supabase_auth.types import AuthOtpResponse # First test for email OTP diff --git a/src/auth/tests/_sync/test_gotrue_admin_api.py b/src/auth/tests/_sync/test_gotrue_admin_api.py index 9bdba6f5..e577aa3f 100644 --- a/src/auth/tests/_sync/test_gotrue_admin_api.py +++ b/src/auth/tests/_sync/test_gotrue_admin_api.py @@ -1,7 +1,6 @@ import uuid import pytest - from supabase_auth.errors import ( AuthApiError, AuthError, diff --git a/src/auth/tests/test_helpers.py b/src/auth/tests/test_helpers.py index 16a213b2..e8ee6635 100644 --- a/src/auth/tests/test_helpers.py +++ b/src/auth/tests/test_helpers.py @@ -6,7 +6,6 @@ import respx from httpx import Headers, HTTPStatusError, Response from pydantic import BaseModel - from supabase_auth.constants import ( API_VERSION_HEADER_NAME, ) @@ -339,7 +338,6 @@ def test_handle_exception_weak_password_branch() -> None: which is logically impossible, so we'll test it by mocking the implementation details. """ import httpx - from supabase_auth.errors import AuthWeakPasswordError from supabase_auth.helpers import handle_exception diff --git a/src/functions/conftest.py b/src/functions/conftest.py index 79b61d44..1f010de0 100644 --- a/src/functions/conftest.py +++ b/src/functions/conftest.py @@ -7,7 +7,7 @@ _test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {} -def pytest_runtest_makereport(item, call): +def pytest_runtest_makereport(item, call) -> None: if "incremental" in item.keywords: # incremental marker is used if call.excinfo is not None: @@ -29,7 +29,7 @@ def pytest_runtest_makereport(item, call): ) -def pytest_runtest_setup(item): +def pytest_runtest_setup(item) -> None: if "incremental" in item.keywords: # retrieve the class name of the test cls_name = str(item.cls) diff --git a/src/functions/run-unasync.py b/src/functions/run-unasync.py index 3ea41c91..61709f54 100644 --- a/src/functions/run-unasync.py +++ b/src/functions/run-unasync.py @@ -1,6 +1,7 @@ -import unasync from pathlib import Path +import unasync + paths = Path("src/supabase_functions").glob("**/*.py") tests = Path("tests").glob("**/*.py") diff --git a/src/functions/src/supabase_functions/_async/functions_client.py b/src/functions/src/supabase_functions/_async/functions_client.py index f35cce58..2887aa9b 100644 --- a/src/functions/src/supabase_functions/_async/functions_client.py +++ b/src/functions/src/supabase_functions/_async/functions_client.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Literal, Optional, Union from warnings import warn -from httpx import AsyncClient, HTTPError, Response, QueryParams +from httpx import AsyncClient, HTTPError, QueryParams, Response from yarl import URL from ..errors import FunctionsHttpError, FunctionsRelayError @@ -22,7 +22,7 @@ def __init__( verify: Optional[bool] = None, proxy: Optional[str] = None, http_client: Optional[AsyncClient] = None, - ): + ) -> None: if not is_http_url(url): raise ValueError("url must be a valid HTTP URL string") self.url = URL(url) @@ -133,7 +133,7 @@ async def invoke( region = invoke_options.get("region") if region: if not isinstance(region, FunctionRegion): - warn(f"Use FunctionRegion({region})") + warn(f"Use FunctionRegion({region})", stacklevel=2) region = FunctionRegion(region) if region.value != "any": diff --git a/src/functions/src/supabase_functions/_sync/functions_client.py b/src/functions/src/supabase_functions/_sync/functions_client.py index a2c12455..e19ef5b3 100644 --- a/src/functions/src/supabase_functions/_sync/functions_client.py +++ b/src/functions/src/supabase_functions/_sync/functions_client.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Literal, Optional, Union from warnings import warn -from httpx import Client, HTTPError, Response, QueryParams +from httpx import Client, HTTPError, QueryParams, Response from yarl import URL from ..errors import FunctionsHttpError, FunctionsRelayError @@ -22,7 +22,7 @@ def __init__( verify: Optional[bool] = None, proxy: Optional[str] = None, http_client: Optional[Client] = None, - ): + ) -> None: if not is_http_url(url): raise ValueError("url must be a valid HTTP URL string") self.url = URL(url) @@ -133,7 +133,7 @@ def invoke( region = invoke_options.get("region") if region: if not isinstance(region, FunctionRegion): - warn(f"Use FunctionRegion({region})") + warn(f"Use FunctionRegion({region})", stacklevel=2) region = FunctionRegion(region) if region.value != "any": diff --git a/src/functions/src/supabase_functions/utils.py b/src/functions/src/supabase_functions/utils.py index 6740c25e..a2a2fdf9 100644 --- a/src/functions/src/supabase_functions/utils.py +++ b/src/functions/src/supabase_functions/utils.py @@ -1,9 +1,7 @@ import sys from urllib.parse import urlparse -from warnings import warn from httpx import AsyncClient as AsyncClient # noqa: F401 -from httpx import Client as BaseClient if sys.version_info >= (3, 11): from enum import StrEnum @@ -33,25 +31,6 @@ class FunctionRegion(StrEnum): UsWest2 = "us-west-2" -class SyncClient(BaseClient): - def __init__(self, *args, **kwargs): - warn( - "The 'SyncClient' class is deprecated. Please use `Client` from the httpx package instead.", - DeprecationWarning, - stacklevel=2, - ) - - super().__init__(*args, **kwargs) - - def aclose(self) -> None: - warn( - "The 'aclose' method is deprecated. Please use `close` method from `Client` in the httpx package instead.", - DeprecationWarning, - stacklevel=2, - ) - self.close() - - def is_valid_str_arg(target: str) -> bool: return isinstance(target, str) and len(target.strip()) > 0 diff --git a/src/functions/tests/_async/test_function_client.py b/src/functions/tests/_async/test_function_client.py index b359b4b6..f2ef8945 100644 --- a/src/functions/tests/_async/test_function_client.py +++ b/src/functions/tests/_async/test_function_client.py @@ -1,3 +1,4 @@ +from typing import Dict from unittest.mock import AsyncMock, Mock, patch import pytest @@ -11,23 +12,25 @@ @pytest.fixture -def valid_url(): +def valid_url() -> str: return "https://example.com" @pytest.fixture -def default_headers(): +def default_headers() -> Dict[str, str]: return {"Authorization": "Bearer valid.jwt.token"} @pytest.fixture -def client(valid_url, default_headers): +def client(valid_url: str, default_headers: Dict[str, str]) -> AsyncFunctionsClient: return AsyncFunctionsClient( url=valid_url, headers=default_headers, timeout=10, verify=True ) -async def test_init_with_valid_params(valid_url, default_headers): +async def test_init_with_valid_params( + valid_url: str, default_headers: Dict[str, str] +) -> None: client = AsyncFunctionsClient( url=valid_url, headers=default_headers, timeout=10, verify=True ) @@ -38,18 +41,20 @@ async def test_init_with_valid_params(valid_url, default_headers): @pytest.mark.parametrize("invalid_url", ["not-a-url", "ftp://invalid.com", "", None]) -def test_init_with_invalid_url(invalid_url, default_headers): +def test_init_with_invalid_url( + invalid_url: str, default_headers: Dict[str, str] +) -> None: with pytest.raises(ValueError, match="url must be a valid HTTP URL string"): AsyncFunctionsClient(url=invalid_url, headers=default_headers, timeout=10) -async def test_set_auth_valid_token(client: AsyncFunctionsClient): +async def test_set_auth_valid_token(client: AsyncFunctionsClient) -> None: valid_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" client.set_auth(valid_token) assert client.headers["Authorization"] == f"Bearer {valid_token}" -async def test_invoke_success_json(client: AsyncFunctionsClient): +async def test_invoke_success_json(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -70,7 +75,7 @@ async def test_invoke_success_json(client: AsyncFunctionsClient): assert kwargs["json"] == {"test": "data"} -async def test_invoke_success_binary(client: AsyncFunctionsClient): +async def test_invoke_success_binary(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.content = b"binary content" mock_response.raise_for_status = Mock() @@ -87,7 +92,7 @@ async def test_invoke_success_binary(client: AsyncFunctionsClient): mock_request.assert_called_once() -async def test_invoke_with_region(client: AsyncFunctionsClient): +async def test_invoke_with_region(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -107,7 +112,7 @@ async def test_invoke_with_region(client: AsyncFunctionsClient): assert kwargs["params"]["forceFunctionRegion"] == "us-east-1" -async def test_invoke_with_region_string(client: AsyncFunctionsClient): +async def test_invoke_with_region_string(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -128,7 +133,7 @@ async def test_invoke_with_region_string(client: AsyncFunctionsClient): assert kwargs["params"]["forceFunctionRegion"] == "us-east-1" -async def test_invoke_with_http_error(client: AsyncFunctionsClient): +async def test_invoke_with_http_error(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"error": "Custom error message"} mock_response.raise_for_status.side_effect = HTTPError("HTTP Error") @@ -143,7 +148,7 @@ async def test_invoke_with_http_error(client: AsyncFunctionsClient): await client.invoke("test-function") -async def test_invoke_with_relay_error(client: AsyncFunctionsClient): +async def test_invoke_with_relay_error(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"error": "Relay error message"} mock_response.raise_for_status = Mock() @@ -158,12 +163,12 @@ async def test_invoke_with_relay_error(client: AsyncFunctionsClient): await client.invoke("test-function") -async def test_invoke_invalid_function_name(client: AsyncFunctionsClient): +async def test_invoke_invalid_function_name(client: AsyncFunctionsClient) -> None: with pytest.raises(ValueError, match="function_name must a valid string value."): await client.invoke("") -async def test_invoke_with_string_body(client: AsyncFunctionsClient): +async def test_invoke_with_string_body(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -180,7 +185,7 @@ async def test_invoke_with_string_body(client: AsyncFunctionsClient): assert kwargs["headers"]["Content-Type"] == "text/plain" -async def test_invoke_with_json_body(client: AsyncFunctionsClient): +async def test_invoke_with_json_body(client: AsyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -197,7 +202,7 @@ async def test_invoke_with_json_body(client: AsyncFunctionsClient): assert kwargs["headers"]["Content-Type"] == "application/json" -async def test_init_with_httpx_client(): +async def test_init_with_httpx_client() -> None: # Create a custom httpx client with specific options headers = {"x-user-agent": "my-app/0.0.1"} custom_client = AsyncClient( diff --git a/src/functions/tests/_sync/test_function_client.py b/src/functions/tests/_sync/test_function_client.py index 1ed4cc98..7d346807 100644 --- a/src/functions/tests/_sync/test_function_client.py +++ b/src/functions/tests/_sync/test_function_client.py @@ -1,3 +1,4 @@ +from typing import Dict from unittest.mock import Mock, patch import pytest @@ -11,23 +12,25 @@ @pytest.fixture -def valid_url(): +def valid_url() -> str: return "https://example.com" @pytest.fixture -def default_headers(): +def default_headers() -> Dict[str, str]: return {"Authorization": "Bearer valid.jwt.token"} @pytest.fixture -def client(valid_url, default_headers): +def client(valid_url: str, default_headers: Dict[str, str]) -> SyncFunctionsClient: return SyncFunctionsClient( url=valid_url, headers=default_headers, timeout=10, verify=True ) -def test_init_with_valid_params(valid_url, default_headers): +def test_init_with_valid_params( + valid_url: str, default_headers: Dict[str, str] +) -> None: client = SyncFunctionsClient( url=valid_url, headers=default_headers, timeout=10, verify=True ) @@ -38,18 +41,20 @@ def test_init_with_valid_params(valid_url, default_headers): @pytest.mark.parametrize("invalid_url", ["not-a-url", "ftp://invalid.com", "", None]) -def test_init_with_invalid_url(invalid_url, default_headers): +def test_init_with_invalid_url( + invalid_url: str, default_headers: Dict[str, str] +) -> None: with pytest.raises(ValueError, match="url must be a valid HTTP URL string"): SyncFunctionsClient(url=invalid_url, headers=default_headers, timeout=10) -def test_set_auth_valid_token(client: SyncFunctionsClient): +def test_set_auth_valid_token(client: SyncFunctionsClient) -> None: valid_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" client.set_auth(valid_token) assert client.headers["Authorization"] == f"Bearer {valid_token}" -def test_invoke_success_json(client: SyncFunctionsClient): +def test_invoke_success_json(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -68,7 +73,7 @@ def test_invoke_success_json(client: SyncFunctionsClient): assert kwargs["json"] == {"test": "data"} -def test_invoke_success_binary(client: SyncFunctionsClient): +def test_invoke_success_binary(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.content = b"binary content" mock_response.raise_for_status = Mock() @@ -83,7 +88,7 @@ def test_invoke_success_binary(client: SyncFunctionsClient): mock_request.assert_called_once() -def test_invoke_with_region(client: SyncFunctionsClient): +def test_invoke_with_region(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -101,7 +106,7 @@ def test_invoke_with_region(client: SyncFunctionsClient): assert kwargs["params"]["forceFunctionRegion"] == "us-east-1" -def test_invoke_with_region_string(client: SyncFunctionsClient): +def test_invoke_with_region_string(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -120,7 +125,7 @@ def test_invoke_with_region_string(client: SyncFunctionsClient): assert kwargs["params"]["forceFunctionRegion"] == "us-east-1" -def test_invoke_with_http_error(client: SyncFunctionsClient): +def test_invoke_with_http_error(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"error": "Custom error message"} mock_response.raise_for_status.side_effect = HTTPError("HTTP Error") @@ -133,7 +138,7 @@ def test_invoke_with_http_error(client: SyncFunctionsClient): client.invoke("test-function") -def test_invoke_with_relay_error(client: SyncFunctionsClient): +def test_invoke_with_relay_error(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"error": "Relay error message"} mock_response.raise_for_status = Mock() @@ -146,12 +151,12 @@ def test_invoke_with_relay_error(client: SyncFunctionsClient): client.invoke("test-function") -def test_invoke_invalid_function_name(client: SyncFunctionsClient): +def test_invoke_invalid_function_name(client: SyncFunctionsClient) -> None: with pytest.raises(ValueError, match="function_name must a valid string value."): client.invoke("") -def test_invoke_with_string_body(client: SyncFunctionsClient): +def test_invoke_with_string_body(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -166,7 +171,7 @@ def test_invoke_with_string_body(client: SyncFunctionsClient): assert kwargs["headers"]["Content-Type"] == "text/plain" -def test_invoke_with_json_body(client: SyncFunctionsClient): +def test_invoke_with_json_body(client: SyncFunctionsClient) -> None: mock_response = Mock(spec=Response) mock_response.json.return_value = {"message": "success"} mock_response.raise_for_status = Mock() @@ -181,7 +186,7 @@ def test_invoke_with_json_body(client: SyncFunctionsClient): assert kwargs["headers"]["Content-Type"] == "application/json" -def test_init_with_httpx_client(): +def test_init_with_httpx_client() -> None: # Create a custom httpx client with specific options headers = {"x-user-agent": "my-app/0.0.1"} custom_client = Client( diff --git a/src/functions/tests/test_client.py b/src/functions/tests/test_client.py index 804f3708..80a200e4 100644 --- a/src/functions/tests/test_client.py +++ b/src/functions/tests/test_client.py @@ -1,7 +1,6 @@ from typing import Dict import pytest - from supabase_functions import AsyncFunctionsClient, SyncFunctionsClient, create_client @@ -15,7 +14,7 @@ def valid_headers() -> Dict[str, str]: return {"Authorization": "Bearer test_token", "Content-Type": "application/json"} -def test_create_async_client(valid_url, valid_headers): +def test_create_async_client(valid_url: str, valid_headers: Dict[str, str]) -> None: # Test creating async client with explicit verify=True client = create_client( url=valid_url, headers=valid_headers, is_async=True, verify=True @@ -26,7 +25,7 @@ def test_create_async_client(valid_url, valid_headers): assert all(client.headers[key] == value for key, value in valid_headers.items()) -def test_create_sync_client(valid_url, valid_headers): +def test_create_sync_client(valid_url: str, valid_headers: Dict[str, str]) -> None: # Test creating sync client with explicit verify=True client = create_client( url=valid_url, headers=valid_headers, is_async=False, verify=True @@ -37,7 +36,7 @@ def test_create_sync_client(valid_url, valid_headers): assert all(client.headers[key] == value for key, value in valid_headers.items()) -def test_type_hints(): +def test_type_hints() -> None: from typing import Union, get_type_hints hints = get_type_hints(create_client) diff --git a/src/functions/tests/test_errors.py b/src/functions/tests/test_errors.py index 8401a0c3..11eca4e0 100644 --- a/src/functions/tests/test_errors.py +++ b/src/functions/tests/test_errors.py @@ -1,7 +1,6 @@ from typing import Type import pytest - from supabase_functions.errors import ( FunctionsApiErrorDict, FunctionsError, @@ -20,7 +19,7 @@ ) def test_error_initialization( error_class: Type[FunctionsError], expected_name: str, expected_status: int -): +) -> None: test_message = "Test error message" if issubclass(error_class, (FunctionsHttpError, FunctionsRelayError)): error: FunctionsError = error_class(test_message) @@ -44,7 +43,7 @@ def test_error_initialization( ) def test_error_to_dict( error_class: Type[FunctionsError], expected_name: str, expected_status: int -): +) -> None: test_message = "Test error message" if issubclass(error_class, (FunctionsHttpError, FunctionsRelayError)): @@ -66,13 +65,13 @@ def test_error_to_dict( assert isinstance(typed_dict["status"], int) -def test_functions_error_inheritance(): +def test_functions_error_inheritance() -> None: # Test that all error classes inherit from FunctionsError assert issubclass(FunctionsHttpError, FunctionsError) assert issubclass(FunctionsRelayError, FunctionsError) -def test_error_as_exception(): +def test_error_as_exception() -> None: # Test that errors can be raised and caught test_message = "Test exception" @@ -92,7 +91,7 @@ def test_error_as_exception(): assert str(exc_info.value) == test_message -def test_error_message_types(): +def test_error_message_types() -> None: # Test that errors handle different message types appropriately test_cases = [ "Simple string", diff --git a/src/functions/tests/test_utils.py b/src/functions/tests/test_utils.py index 581b5e3a..cbaaa736 100644 --- a/src/functions/tests/test_utils.py +++ b/src/functions/tests/test_utils.py @@ -2,17 +2,15 @@ from typing import Any import pytest - from supabase_functions.utils import ( BASE64URL_REGEX, FunctionRegion, - SyncClient, is_http_url, is_valid_str_arg, ) -def test_function_region_values(): +def test_function_region_values() -> None: assert FunctionRegion.Any.value == "any" assert FunctionRegion.ApNortheast1.value == "ap-northeast-1" assert FunctionRegion.ApNortheast2.value == "ap-northeast-2" @@ -30,14 +28,6 @@ def test_function_region_values(): assert FunctionRegion.UsWest2.value == "us-west-2" -def test_sync_client(): - client = SyncClient() - # Verify that aclose method exists and calls close - assert hasattr(client, "aclose") - assert callable(client.aclose) - client.aclose() # Should not raise any exception - - @pytest.mark.parametrize( "test_input,expected", [ @@ -51,7 +41,7 @@ def test_sync_client(): ({}, False), ], ) -def test_is_valid_str_arg(test_input: Any, expected: bool): +def test_is_valid_str_arg(test_input: Any, expected: bool) -> None: assert is_valid_str_arg(test_input) == expected @@ -68,11 +58,11 @@ def test_is_valid_str_arg(test_input: Any, expected: bool): ("", False), ], ) -def test_is_http_url(test_input: str, expected: bool): +def test_is_http_url(test_input: str, expected: bool) -> None: assert is_http_url(test_input) == expected -def test_base64url_regex(): +def test_base64url_regex() -> None: import re pattern = re.compile(BASE64URL_REGEX, re.IGNORECASE) @@ -99,8 +89,8 @@ def test_base64url_regex(): sys.version_info < (3, 11), reason="StrEnum import test only relevant for Python 3.11+", ) -def test_strenum_import_python_311_plus(): - from enum import StrEnum as BuiltinStrEnum +def test_strenum_import_python_311_plus() -> None: + from enum import StrEnum as BuiltinStrEnum # type: ignore assert isinstance(FunctionRegion.Any, BuiltinStrEnum) @@ -109,7 +99,7 @@ def test_strenum_import_python_311_plus(): sys.version_info >= (3, 11), reason="strenum import test only relevant for Python < 3.11", ) -def test_strenum_import_python_310_and_below(): +def test_strenum_import_python_310_and_below() -> None: from strenum import StrEnum as ExternalStrEnum assert isinstance(FunctionRegion.Any, ExternalStrEnum) diff --git a/src/storage/pyproject.toml b/src/storage/pyproject.toml index 553030a0..48a3b315 100644 --- a/src/storage/pyproject.toml +++ b/src/storage/pyproject.toml @@ -59,28 +59,6 @@ dev = [ { include-group = "docs" }, ] -[tool.ruff.lint] -select = [ - # pycodestyle - "E", - # Pyflakes - "F", - # pyupgrade - "UP", - # flake8-bugbear - # "B", - # flake8-simplify - # "SIM", - # isort - "I", -] -ignore = ["F401", "F403", "F841", "E712", "E501", "E402", "UP006", "UP035"] -# isort.required-imports = ["from __future__ import annotations"] - -[tool.ruff.lint.pyupgrade] -# Preserve types, even if a file imports `from __future__ import annotations`. -keep-runtime-typing = true - [tool.pytest.ini_options] asyncio_mode = "auto" addopts = "tests" diff --git a/src/storage/src/storage3/_async/bucket.py b/src/storage/src/storage3/_async/bucket.py index d5e00a4e..5c330118 100644 --- a/src/storage/src/storage3/_async/bucket.py +++ b/src/storage/src/storage3/_async/bucket.py @@ -37,7 +37,9 @@ async def _request( response.raise_for_status() except HTTPStatusError as exc: resp = exc.response.json() - raise StorageApiError(resp["message"], resp["error"], resp["statusCode"]) + raise StorageApiError( + resp["message"], resp["error"], resp["statusCode"] + ) from exc return response diff --git a/src/storage/src/storage3/_async/file_api.py b/src/storage/src/storage3/_async/file_api.py index 4993671a..9d2b314b 100644 --- a/src/storage/src/storage3/_async/file_api.py +++ b/src/storage/src/storage3/_async/file_api.py @@ -78,7 +78,9 @@ async def _request( response.raise_for_status() except HTTPStatusError as exc: resp = exc.response.json() - raise StorageApiError(resp["message"], resp["error"], resp["statusCode"]) + raise StorageApiError( + resp["message"], resp["error"], resp["statusCode"] + ) from exc # close the resource before returning the response if files and "file" in files and isinstance(files["file"][1], BufferedReader): @@ -200,7 +202,7 @@ def _make_signed_url( return {"signedURL": str(signedURL), "signedUrl": str(signedURL)} async def create_signed_url( - self, path: str, expires_in: int, options: URLOptions = {} + self, path: str, expires_in: int, options: Optional[URLOptions] = None ) -> SignedUrlResponse: """ Parameters @@ -214,10 +216,11 @@ async def create_signed_url( """ json: dict[str, str | bool | TransformOptions] = {"expiresIn": str(expires_in)} download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): json.update({"download": download}) download_query = {"download": "" if download is True else download} - if transform := options.get("transform"): + if transform := url_options.get("transform"): json.update({"transform": transform}) path_parts = relative_path_to_parts(path) @@ -231,7 +234,10 @@ async def create_signed_url( return self._make_signed_url(data.signedURL, download_query) async def create_signed_urls( - self, paths: List[str], expires_in: int, options: CreateSignedURLsOptions = {} + self, + paths: List[str], + expires_in: int, + options: Optional[CreateSignedURLsOptions] = None, ) -> List[CreateSignedUrlResponse]: """ Parameters @@ -248,7 +254,8 @@ async def create_signed_urls( "expiresIn": str(expires_in), } download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): json.update({"download": download}) download_query = {"download": "" if download is True else download} @@ -271,7 +278,9 @@ async def create_signed_urls( signed_urls.append(signed_item) return signed_urls - async def get_public_url(self, path: str, options: URLOptions = {}) -> str: + async def get_public_url( + self, path: str, options: Optional[URLOptions] = None + ) -> str: """ Parameters ---------- @@ -279,12 +288,15 @@ async def get_public_url(self, path: str, options: URLOptions = {}) -> str: file path, including the path and file name. For example `folder/image.png`. """ download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): download_query = {"download": "" if download is True else download} - render_path = ["render", "image"] if options.get("transform") else ["object"] + render_path = ( + ["render", "image"] if url_options.get("transform") else ["object"] + ) transformation = ( - transform_to_dict(t) if (t := options.get("transform")) else dict() + transform_to_dict(t) if (t := url_options.get("transform")) else dict() ) path_parts = relative_path_to_parts(path) @@ -426,7 +438,9 @@ async def list( ) return response.json() - async def download(self, path: str, options: DownloadOptions = {}) -> bytes: + async def download( + self, path: str, options: Optional[DownloadOptions] = None + ) -> bytes: """ Downloads a file. @@ -435,13 +449,14 @@ async def download(self, path: str, options: DownloadOptions = {}) -> bytes: path The file path to be downloaded, including the path and file name. For example `folder/image.png`. """ + url_options = options or {} render_path = ( ["render", "image", "authenticated"] - if options.get("transform") + if url_options.get("transform") else ["object"] ) - transform_options = options.get("transform") or {} + transform_options = url_options.get("transform") or {} path_parts = relative_path_to_parts(path) response = await self._request( diff --git a/src/storage/src/storage3/_sync/bucket.py b/src/storage/src/storage3/_sync/bucket.py index d0dc13ec..22248a95 100644 --- a/src/storage/src/storage3/_sync/bucket.py +++ b/src/storage/src/storage3/_sync/bucket.py @@ -37,7 +37,9 @@ def _request( response.raise_for_status() except HTTPStatusError as exc: resp = exc.response.json() - raise StorageApiError(resp["message"], resp["error"], resp["statusCode"]) + raise StorageApiError( + resp["message"], resp["error"], resp["statusCode"] + ) from exc return response diff --git a/src/storage/src/storage3/_sync/file_api.py b/src/storage/src/storage3/_sync/file_api.py index b1be7bcc..2d72d3cb 100644 --- a/src/storage/src/storage3/_sync/file_api.py +++ b/src/storage/src/storage3/_sync/file_api.py @@ -78,7 +78,9 @@ def _request( response.raise_for_status() except HTTPStatusError as exc: resp = exc.response.json() - raise StorageApiError(resp["message"], resp["error"], resp["statusCode"]) + raise StorageApiError( + resp["message"], resp["error"], resp["statusCode"] + ) from exc # close the resource before returning the response if files and "file" in files and isinstance(files["file"][1], BufferedReader): @@ -200,7 +202,7 @@ def _make_signed_url( return {"signedURL": str(signedURL), "signedUrl": str(signedURL)} def create_signed_url( - self, path: str, expires_in: int, options: URLOptions = {} + self, path: str, expires_in: int, options: Optional[URLOptions] = None ) -> SignedUrlResponse: """ Parameters @@ -214,10 +216,11 @@ def create_signed_url( """ json: dict[str, str | bool | TransformOptions] = {"expiresIn": str(expires_in)} download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): json.update({"download": download}) download_query = {"download": "" if download is True else download} - if transform := options.get("transform"): + if transform := url_options.get("transform"): json.update({"transform": transform}) path_parts = relative_path_to_parts(path) @@ -231,7 +234,10 @@ def create_signed_url( return self._make_signed_url(data.signedURL, download_query) def create_signed_urls( - self, paths: List[str], expires_in: int, options: CreateSignedURLsOptions = {} + self, + paths: List[str], + expires_in: int, + options: Optional[CreateSignedURLsOptions] = None, ) -> List[CreateSignedUrlResponse]: """ Parameters @@ -248,7 +254,8 @@ def create_signed_urls( "expiresIn": str(expires_in), } download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): json.update({"download": download}) download_query = {"download": "" if download is True else download} @@ -271,7 +278,7 @@ def create_signed_urls( signed_urls.append(signed_item) return signed_urls - def get_public_url(self, path: str, options: URLOptions = {}) -> str: + def get_public_url(self, path: str, options: Optional[URLOptions] = None) -> str: """ Parameters ---------- @@ -279,12 +286,15 @@ def get_public_url(self, path: str, options: URLOptions = {}) -> str: file path, including the path and file name. For example `folder/image.png`. """ download_query = {} - if download := options.get("download"): + url_options = options or {} + if download := url_options.get("download"): download_query = {"download": "" if download is True else download} - render_path = ["render", "image"] if options.get("transform") else ["object"] + render_path = ( + ["render", "image"] if url_options.get("transform") else ["object"] + ) transformation = ( - transform_to_dict(t) if (t := options.get("transform")) else dict() + transform_to_dict(t) if (t := url_options.get("transform")) else dict() ) path_parts = relative_path_to_parts(path) @@ -426,7 +436,7 @@ def list( ) return response.json() - def download(self, path: str, options: DownloadOptions = {}) -> bytes: + def download(self, path: str, options: Optional[DownloadOptions] = None) -> bytes: """ Downloads a file. @@ -435,13 +445,14 @@ def download(self, path: str, options: DownloadOptions = {}) -> bytes: path The file path to be downloaded, including the path and file name. For example `folder/image.png`. """ + url_options = options or {} render_path = ( ["render", "image", "authenticated"] - if options.get("transform") + if url_options.get("transform") else ["object"] ) - transform_options = options.get("transform") or {} + transform_options = url_options.get("transform") or {} path_parts = relative_path_to_parts(path) response = self._request( diff --git a/src/storage/src/storage3/types.py b/src/storage/src/storage3/types.py index c74417d3..43bf4bba 100644 --- a/src/storage/src/storage3/types.py +++ b/src/storage/src/storage3/types.py @@ -4,7 +4,7 @@ from datetime import datetime from typing import Any, Dict, Literal, Optional, TypedDict, Union -from pydantic import BaseModel, ConfigDict, Field, TypeAdapter +from pydantic import BaseModel, ConfigDict, TypeAdapter from typing_extensions import ReadOnly RequestMethod = Literal["GET", "POST", "DELETE", "PUT", "HEAD"] @@ -104,7 +104,7 @@ class UploadResponse: full_path: str fullPath: str - def __init__(self, path: str, Key: str): + def __init__(self, path: str, Key: str) -> None: self.path = path self.full_path = Key self.fullPath = Key diff --git a/src/storage/src/storage3/utils.py b/src/storage/src/storage3/utils.py index 6013fda5..0b323ec2 100644 --- a/src/storage/src/storage3/utils.py +++ b/src/storage/src/storage3/utils.py @@ -9,7 +9,7 @@ class SyncClient(Client): @deprecated( "0.11.3", "3.0.0", __version__, "Use `Client` from the httpx package instead" ) - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) @deprecated( diff --git a/src/storage/tests/_async/conftest.py b/src/storage/tests/_async/conftest.py index 34b4a356..1b8b6bc6 100644 --- a/src/storage/tests/_async/conftest.py +++ b/src/storage/tests/_async/conftest.py @@ -1,12 +1,10 @@ from __future__ import annotations -import asyncio import os -from collections.abc import AsyncGenerator, Generator +from collections.abc import AsyncGenerator import pytest from dotenv import load_dotenv - from storage3 import AsyncStorageClient diff --git a/src/storage/tests/_async/test_bucket.py b/src/storage/tests/_async/test_bucket.py index 5f805570..295c938e 100644 --- a/src/storage/tests/_async/test_bucket.py +++ b/src/storage/tests/_async/test_bucket.py @@ -2,16 +2,13 @@ import pytest from httpx import AsyncClient, Headers, HTTPStatusError, Response - from storage3 import AsyncBucket, AsyncStorageBucketAPI from storage3.exceptions import StorageApiError from storage3.types import CreateOrUpdateBucketOptions -from ..test_client import valid_url - @pytest.fixture -def mock_client(): +def mock_client() -> AsyncMock: return AsyncMock() @@ -26,13 +23,13 @@ def storage_api(mock_client: AsyncClient, headers: Headers) -> AsyncStorageBucke @pytest.fixture -def mock_response(): +def mock_response() -> Mock: response = Mock(spec=Response) response.raise_for_status = Mock() return response -async def test_list_buckets(storage_api, mock_client, mock_response): +async def test_list_buckets(storage_api, mock_client, mock_response) -> None: # Mock response data mock_response.json.return_value = [ { @@ -68,7 +65,7 @@ async def test_list_buckets(storage_api, mock_client, mock_response): mock_client.request.assert_called_once_with("GET", "bucket", json=None, headers={}) -async def test_get_bucket(storage_api, mock_client, mock_response): +async def test_get_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "test-bucket" mock_response.json.return_value = { "id": bucket_id, @@ -95,7 +92,7 @@ async def test_get_bucket(storage_api, mock_client, mock_response): ) -async def test_create_bucket(storage_api, mock_client, mock_response): +async def test_create_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "new-bucket" bucket_name = "New Bucket" options = CreateOrUpdateBucketOptions( @@ -122,7 +119,7 @@ async def test_create_bucket(storage_api, mock_client, mock_response): ) -async def test_create_bucket_minimal(storage_api, mock_client, mock_response): +async def test_create_bucket_minimal(storage_api, mock_client, mock_response) -> None: bucket_id = "minimal-bucket" mock_response.json.return_value = {"message": "Bucket created successfully"} mock_client.request.return_value = mock_response @@ -135,7 +132,7 @@ async def test_create_bucket_minimal(storage_api, mock_client, mock_response): ) -async def test_update_bucket(storage_api, mock_client, mock_response): +async def test_update_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "update-bucket" options = CreateOrUpdateBucketOptions(public=False, file_size_limit=2000000) @@ -158,7 +155,7 @@ async def test_update_bucket(storage_api, mock_client, mock_response): ) -async def test_empty_bucket(storage_api, mock_client, mock_response): +async def test_empty_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "empty-bucket" mock_response.json.return_value = {"message": "Bucket emptied successfully"} mock_client.request.return_value = mock_response @@ -171,7 +168,7 @@ async def test_empty_bucket(storage_api, mock_client, mock_response): ) -async def test_delete_bucket(storage_api, mock_client, mock_response): +async def test_delete_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "delete-bucket" mock_response.json.return_value = {"message": "Bucket deleted successfully"} mock_client.request.return_value = mock_response @@ -184,7 +181,7 @@ async def test_delete_bucket(storage_api, mock_client, mock_response): ) -async def test_request_error_handling(storage_api, mock_client): +async def test_request_error_handling(storage_api, mock_client) -> None: error_response = Mock(spec=Response) error_response.json.return_value = { "message": "Test error message", @@ -212,7 +209,7 @@ async def test_request_error_handling(storage_api, mock_client): ) async def test_request_methods( storage_api, mock_client, mock_response, method, path, json_data -): +) -> None: mock_client.request.return_value = mock_response await storage_api._request(method, [path], json_data) mock_client.request.assert_called_once_with( diff --git a/src/storage/tests/_async/test_client.py b/src/storage/tests/_async/test_client.py index 85abddc2..6d934f40 100644 --- a/src/storage/tests/_async/test_client.py +++ b/src/storage/tests/_async/test_client.py @@ -9,7 +9,6 @@ import pytest from httpx import AsyncClient as HttpxClient from httpx import HTTPStatusError, Response - from storage3 import AsyncStorageClient from storage3.exceptions import StorageApiError from storage3.utils import StorageException @@ -39,10 +38,10 @@ def method() -> str: async def delete_left_buckets( request: pytest.FixtureRequest, storage: AsyncStorageClient, -): +) -> None: """Ensures no test buckets are left when a test that created a bucket fails""" - async def afinalizer(): + async def afinalizer() -> None: for bucket_id in temp_test_buckets_ids: try: await storage.empty_bucket(bucket_id) @@ -558,7 +557,7 @@ async def test_client_exists_json_decode_error( """Test exists method handling of json.JSONDecodeError""" from json import JSONDecodeError - async def mock_head(*args, **kwargs): + async def mock_head(*args, **kwargs) -> None: raise JSONDecodeError("Expecting value", "", 0) monkeypatch.setattr(storage_file_client_public._client, "head", mock_head) diff --git a/src/storage/tests/_sync/conftest.py b/src/storage/tests/_sync/conftest.py index 254715f5..598159ef 100644 --- a/src/storage/tests/_sync/conftest.py +++ b/src/storage/tests/_sync/conftest.py @@ -1,12 +1,10 @@ from __future__ import annotations -import asyncio import os from collections.abc import Generator import pytest from dotenv import load_dotenv - from storage3 import SyncStorageClient diff --git a/src/storage/tests/_sync/test_bucket.py b/src/storage/tests/_sync/test_bucket.py index fe0f76cb..45a81724 100644 --- a/src/storage/tests/_sync/test_bucket.py +++ b/src/storage/tests/_sync/test_bucket.py @@ -2,16 +2,13 @@ import pytest from httpx import Client, Headers, HTTPStatusError, Response - from storage3 import SyncBucket, SyncStorageBucketAPI from storage3.exceptions import StorageApiError from storage3.types import CreateOrUpdateBucketOptions -from ..test_client import valid_url - @pytest.fixture -def mock_client(): +def mock_client() -> Mock: return Mock() @@ -26,13 +23,13 @@ def storage_api(mock_client: Client, headers: Headers) -> SyncStorageBucketAPI: @pytest.fixture -def mock_response(): +def mock_response() -> Mock: response = Mock(spec=Response) response.raise_for_status = Mock() return response -def test_list_buckets(storage_api, mock_client, mock_response): +def test_list_buckets(storage_api, mock_client, mock_response) -> None: # Mock response data mock_response.json.return_value = [ { @@ -68,7 +65,7 @@ def test_list_buckets(storage_api, mock_client, mock_response): mock_client.request.assert_called_once_with("GET", "bucket", json=None, headers={}) -def test_get_bucket(storage_api, mock_client, mock_response): +def test_get_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "test-bucket" mock_response.json.return_value = { "id": bucket_id, @@ -95,7 +92,7 @@ def test_get_bucket(storage_api, mock_client, mock_response): ) -def test_create_bucket(storage_api, mock_client, mock_response): +def test_create_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "new-bucket" bucket_name = "New Bucket" options = CreateOrUpdateBucketOptions( @@ -122,7 +119,7 @@ def test_create_bucket(storage_api, mock_client, mock_response): ) -def test_create_bucket_minimal(storage_api, mock_client, mock_response): +def test_create_bucket_minimal(storage_api, mock_client, mock_response) -> None: bucket_id = "minimal-bucket" mock_response.json.return_value = {"message": "Bucket created successfully"} mock_client.request.return_value = mock_response @@ -135,7 +132,7 @@ def test_create_bucket_minimal(storage_api, mock_client, mock_response): ) -def test_update_bucket(storage_api, mock_client, mock_response): +def test_update_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "update-bucket" options = CreateOrUpdateBucketOptions(public=False, file_size_limit=2000000) @@ -158,7 +155,7 @@ def test_update_bucket(storage_api, mock_client, mock_response): ) -def test_empty_bucket(storage_api, mock_client, mock_response): +def test_empty_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "empty-bucket" mock_response.json.return_value = {"message": "Bucket emptied successfully"} mock_client.request.return_value = mock_response @@ -171,7 +168,7 @@ def test_empty_bucket(storage_api, mock_client, mock_response): ) -def test_delete_bucket(storage_api, mock_client, mock_response): +def test_delete_bucket(storage_api, mock_client, mock_response) -> None: bucket_id = "delete-bucket" mock_response.json.return_value = {"message": "Bucket deleted successfully"} mock_client.request.return_value = mock_response @@ -184,7 +181,7 @@ def test_delete_bucket(storage_api, mock_client, mock_response): ) -def test_request_error_handling(storage_api, mock_client): +def test_request_error_handling(storage_api, mock_client) -> None: error_response = Mock(spec=Response) error_response.json.return_value = { "message": "Test error message", @@ -212,7 +209,7 @@ def test_request_error_handling(storage_api, mock_client): ) def test_request_methods( storage_api, mock_client, mock_response, method, path, json_data -): +) -> None: mock_client.request.return_value = mock_response storage_api._request(method, [path], json_data) mock_client.request.assert_called_once_with( diff --git a/src/storage/tests/_sync/test_client.py b/src/storage/tests/_sync/test_client.py index b549db0f..34aa4128 100644 --- a/src/storage/tests/_sync/test_client.py +++ b/src/storage/tests/_sync/test_client.py @@ -9,7 +9,6 @@ import pytest from httpx import Client as HttpxClient from httpx import HTTPStatusError, Response - from storage3 import SyncStorageClient from storage3.exceptions import StorageApiError from storage3.utils import StorageException @@ -39,10 +38,10 @@ def method() -> str: def delete_left_buckets( request: pytest.FixtureRequest, storage: SyncStorageClient, -): +) -> None: """Ensures no test buckets are left when a test that created a bucket fails""" - def afinalizer(): + def afinalizer() -> None: for bucket_id in temp_test_buckets_ids: try: storage.empty_bucket(bucket_id) @@ -556,7 +555,7 @@ def test_client_exists_json_decode_error( """Test exists method handling of json.JSONDecodeError""" from json import JSONDecodeError - def mock_head(*args, **kwargs): + def mock_head(*args, **kwargs) -> None: raise JSONDecodeError("Expecting value", "", 0) monkeypatch.setattr(storage_file_client_public._client, "head", mock_head) diff --git a/src/storage/tests/test_client.py b/src/storage/tests/test_client.py index 5fe321a8..4d926cc6 100644 --- a/src/storage/tests/test_client.py +++ b/src/storage/tests/test_client.py @@ -2,7 +2,6 @@ import pytest from httpx import AsyncClient, Client, Timeout - from storage3 import AsyncStorageClient, SyncStorageClient from storage3.constants import DEFAULT_TIMEOUT @@ -17,7 +16,7 @@ def valid_headers() -> Dict[str, str]: return {"Authorization": "Bearer test_token", "apikey": "test_api_key"} -def test_create_async_client(valid_url, valid_headers): +def test_create_async_client(valid_url, valid_headers) -> None: client = AsyncStorageClient(url=valid_url, headers=valid_headers) assert isinstance(client, AsyncStorageClient) @@ -27,7 +26,7 @@ def test_create_async_client(valid_url, valid_headers): assert client._client.timeout == Timeout(DEFAULT_TIMEOUT) -def test_create_sync_client(valid_url, valid_headers): +def test_create_sync_client(valid_url, valid_headers) -> None: client = SyncStorageClient(url=valid_url, headers=valid_headers) assert isinstance(client, SyncStorageClient) @@ -37,7 +36,7 @@ def test_create_sync_client(valid_url, valid_headers): assert client._client.timeout == Timeout(DEFAULT_TIMEOUT) -def test_async_storage_client(valid_url, valid_headers): +def test_async_storage_client(valid_url, valid_headers) -> None: headers = {"x-user-agent": "my-app/0.0.1"} http_client = AsyncClient(headers=headers) client = AsyncStorageClient( @@ -50,7 +49,7 @@ def test_async_storage_client(valid_url, valid_headers): assert client._client.timeout == Timeout(5.0) -def test_sync_storage_client(valid_url, valid_headers): +def test_sync_storage_client(valid_url, valid_headers) -> None: headers = {"x-user-agent": "my-app/0.0.1"} http_client = Client(headers=headers) client = SyncStorageClient( @@ -63,7 +62,7 @@ def test_sync_storage_client(valid_url, valid_headers): assert client._client.timeout == Timeout(5.0) -def test_async_storage_client_with_httpx(valid_url, valid_headers): +def test_async_storage_client_with_httpx(valid_url, valid_headers) -> None: client = AsyncStorageClient(url=valid_url, headers=valid_headers) assert isinstance(client, AsyncStorageClient) @@ -73,7 +72,7 @@ def test_async_storage_client_with_httpx(valid_url, valid_headers): assert client._client.timeout == Timeout(DEFAULT_TIMEOUT) -def test_sync_storage_client_with_httpx(valid_url, valid_headers): +def test_sync_storage_client_with_httpx(valid_url, valid_headers) -> None: client = SyncStorageClient(url=valid_url, headers=valid_headers) assert isinstance(client, SyncStorageClient) @@ -83,7 +82,7 @@ def test_sync_storage_client_with_httpx(valid_url, valid_headers): assert client._client.timeout == Timeout(DEFAULT_TIMEOUT) -def test_custom_timeout(valid_url, valid_headers): +def test_custom_timeout(valid_url, valid_headers) -> None: custom_timeout = 30 async_client = AsyncStorageClient( diff --git a/src/storage/tests/test_exceptions.py b/src/storage/tests/test_exceptions.py index de9031d0..b59ca79e 100644 --- a/src/storage/tests/test_exceptions.py +++ b/src/storage/tests/test_exceptions.py @@ -1,7 +1,7 @@ from storage3.exceptions import StorageApiError -def test_storage_api_error_initialization(): +def test_storage_api_error_initialization() -> None: # Arrange message = "Test error message" code = "TEST_ERROR" @@ -21,7 +21,7 @@ def test_storage_api_error_initialization(): ) -def test_storage_api_error_to_dict(): +def test_storage_api_error_to_dict() -> None: # Arrange error = StorageApiError("Test message", "TEST_CODE", 404) @@ -37,7 +37,7 @@ def test_storage_api_error_to_dict(): } -def test_storage_api_error_inheritance(): +def test_storage_api_error_inheritance() -> None: # Arrange & Act error = StorageApiError("Test message", "TEST_CODE", 500) diff --git a/src/storage/tests/test_utils.py b/src/storage/tests/test_utils.py index a6f308f5..a63709ac 100644 --- a/src/storage/tests/test_utils.py +++ b/src/storage/tests/test_utils.py @@ -1,10 +1,9 @@ from deprecation import fail_if_not_removed - from storage3.utils import SyncClient @fail_if_not_removed -def test_sync_client(): +def test_sync_client() -> None: client = SyncClient() # Verify that aclose method exists and calls close assert hasattr(client, "aclose") diff --git a/src/storage/tests/utils.py b/src/storage/tests/utils.py index 4705cf2f..4acf8390 100644 --- a/src/storage/tests/utils.py +++ b/src/storage/tests/utils.py @@ -3,8 +3,8 @@ class AsyncFinalizerFactory: - def __init__(self, finalizer: Callable[[], Coroutine[Any, Any, None]]): - def func(): + def __init__(self, finalizer: Callable[[], Coroutine[Any, Any, None]]) -> None: + def func() -> Any: event_loop = asyncio.get_event_loop_policy().get_event_loop() return event_loop.run_until_complete(finalizer()) @@ -12,5 +12,5 @@ def func(): class SyncFinalizerFactory: - def __init__(self, finalizer: Callable[[], None]): + def __init__(self, finalizer: Callable[[], None]) -> None: self.finalizer = finalizer