From d7d970b5004ef198948c2af2d15603ec131eb31f Mon Sep 17 00:00:00 2001 From: Kyle Montemayor Date: Tue, 2 Jun 2026 18:39:12 +0000 Subject: [PATCH] Narrow storage.Client.project to str once in GcsUtils.__init__ storage.Client.__init__ raises if no project can be determined, so the .project attribute is always a str at the next line. The upstream stub in google-cloud-storage annotates it as Optional[str], forcing a ty ignore at every call site. Narrow once in __init__ with a fail-fast TypeError guard (per CLAUDE.md) and reuse via self.__project: str. Patches tests/unit/utils/gcs_test.py to set mock_client.project on the two MagicMock(spec=Client) fixtures so the runtime guard accepts them. Resolves 1 `# ty: ignore[invalid-argument-type]` at gcs.py:136. Co-Authored-By: Claude Opus 4.7 (1M context) --- gigl/common/utils/gcs.py | 12 ++++++++++-- tests/unit/utils/gcs_test.py | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/gigl/common/utils/gcs.py b/gigl/common/utils/gcs.py index dfb166afa..a97e732de 100644 --- a/gigl/common/utils/gcs.py +++ b/gigl/common/utils/gcs.py @@ -84,6 +84,14 @@ def __init__(self, project: Optional[str] = None) -> None: project (Optional[str]): The GCP project ID. Defaults to None. """ self.__storage_client = storage.Client(project=project) + # Upstream stub types .project as Optional[str], but Client.__init__ + # raises if no project can be determined — so it's always a str here. + project = self.__storage_client.project + if not isinstance(project, str): + raise TypeError( + f"Expected storage client project to be a str, got {type(project).__name__}" + ) + self.__project: str = project def upload_from_string(self, gcs_path: GcsUri, content: str) -> None: bucket_name, blob_name = self.get_bucket_and_blob_path_from_gcs_path(gcs_path) @@ -133,7 +141,7 @@ def upload_files_to_gcs( """ if parallel: _upload_files_to_gcs_parallel( - project=self.__storage_client.project, # ty: ignore[invalid-argument-type] + project=self.__project, local_file_path_to_gcs_path_map=local_file_path_to_gcs_path_map, ) else: @@ -144,7 +152,7 @@ def upload_files_to_gcs( _upload_file_to_gcs( source_file_path=source_file_path, dest_gcs_path=dest_gcs_path, - project=self.__storage_client.project, + project=self.__project, gcs_utils_client=self.__storage_client, ) diff --git a/tests/unit/utils/gcs_test.py b/tests/unit/utils/gcs_test.py index 1a55978d3..1acaa02bf 100644 --- a/tests/unit/utils/gcs_test.py +++ b/tests/unit/utils/gcs_test.py @@ -16,6 +16,7 @@ class TestGcsUtils(TestCase): def test_upload_from_filelike(self, mock_storage_client): # Mock the GCS client, bucket, and blob mock_client = MagicMock(spec=Client) + mock_client.project = "test-project" mock_bucket = MagicMock(spec=Bucket) mock_blob = MagicMock(spec=Blob) @@ -43,6 +44,7 @@ def test_upload_from_filelike(self, mock_storage_client): def test_delete_files_in_bucket_dir(self): # Mock the GCS client, bucket, and blob mock_client = MagicMock(spec=Client) + mock_client.project = "test-project" mock_bucket = MagicMock(spec=Bucket) non_existent_bucket = "test-bucket"