| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -158,3 +158,66 @@ async def delete_object( | |||
| 158 | 158 | **kwargs, | |
| 159 | 159 | ) | |
| 160 | 160 | await self._grpc_client.delete_object(request=request) | |
| 161 | + | ||
| 162 | + async def get_object( | ||
| 163 | + self, | ||
| 164 | + bucket_name, | ||
| 165 | + object_name, | ||
| 166 | + generation=None, | ||
| 167 | + if_generation_match=None, | ||
| 168 | + if_generation_not_match=None, | ||
| 169 | + if_metageneration_match=None, | ||
| 170 | + if_metageneration_not_match=None, | ||
| 171 | + soft_deleted=None, | ||
| 172 | + **kwargs, | ||
| 173 | + ): | ||
| 174 | + """Retrieves an object's metadata. | ||
| 175 | + | ||
| 176 | + In the gRPC API, this is performed by the GetObject RPC, which | ||
| 177 | + returns the object resource (metadata) without the object's data. | ||
| 178 | + | ||
| 179 | + :type bucket_name: str | ||
| 180 | + :param bucket_name: The name of the bucket in which the object resides. | ||
| 181 | + | ||
| 182 | + :type object_name: str | ||
| 183 | + :param object_name: The name of the object. | ||
| 184 | + | ||
| 185 | + :type generation: int | ||
| 186 | + :param generation: | ||
| 187 | + (Optional) If present, selects a specific generation of an object. | ||
| 188 | + | ||
| 189 | + :type if_generation_match: int | ||
| 190 | + :param if_generation_match: (Optional) Precondition for object generation match. | ||
| 191 | + | ||
| 192 | + :type if_generation_not_match: int | ||
| 193 | + :param if_generation_not_match: (Optional) Precondition for object generation mismatch. | ||
| 194 | + | ||
| 195 | + :type if_metageneration_match: int | ||
| 196 | + :param if_metageneration_match: (Optional) Precondition for metageneration match. | ||
| 197 | + | ||
| 198 | + :type if_metageneration_not_match: int | ||
| 199 | + :param if_metageneration_not_match: (Optional) Precondition for metageneration mismatch. | ||
| 200 | + | ||
| 201 | + :type soft_deleted: bool | ||
| 202 | + :param soft_deleted: | ||
| 203 | + (Optional) If True, return the soft-deleted version of this object. | ||
| 204 | + | ||
| 205 | + :rtype: :class:`google.cloud._storage_v2.types.Object` | ||
| 206 | + :returns: The object metadata resource. | ||
| 207 | + """ | ||
| 208 | + bucket_path = f"projects/_/buckets/{bucket_name}" | ||
| 209 | + | ||
| 210 | + request = storage_v2.GetObjectRequest( | ||
| 211 | + bucket=bucket_path, | ||
| 212 | + object=object_name, | ||
| 213 | + generation=generation, | ||
| 214 | + if_generation_match=if_generation_match, | ||
| 215 | + if_generation_not_match=if_generation_not_match, | ||
| 216 | + if_metageneration_match=if_metageneration_match, | ||
| 217 | + if_metageneration_not_match=if_metageneration_not_match, | ||
| 218 | + soft_deleted=soft_deleted or False, | ||
| 219 | + **kwargs, | ||
| 220 | + ) | ||
| 221 | + | ||
| 222 | + # Calls the underlying GAPIC StorageAsyncClient.get_object method | ||
| 223 | + return await self._grpc_client.get_object(request=request) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -601,3 +601,56 @@ async def _run(): | |||
| 601 | 601 | gc.collect() | |
| 602 | 602 | ||
| 603 | 603 | event_loop.run_until_complete(_run()) | |
| 604 | + | ||
| 605 | + def test_get_object_after_appendable_write( | ||
| 606 | + grpc_clients, | ||
| 607 | + grpc_client_direct, | ||
| 608 | + event_loop, | ||
| 609 | + storage_client, | ||
| 610 | + blobs_to_delete, | ||
| 611 | + ): | ||
| 612 | + """Test getting object metadata after writing with AsyncAppendableObjectWriter. | ||
| 613 | + | ||
| 614 | + This test: | ||
| 615 | + 1. Creates a test object using AsyncAppendableObjectWriter | ||
| 616 | + 2. Appends content to the object (without finalizing) | ||
| 617 | + 3. Closes the write stream | ||
| 618 | + 4. Fetches the object metadata using AsyncGrpcClient.get_object() | ||
| 619 | + 5. Verifies the object size matches the written data | ||
| 620 | + """ | ||
| 621 | + | ||
| 622 | + async def _run(): | ||
| 623 | + grpc_client = grpc_client_direct | ||
| 624 | + object_name = f"test-get-object-{uuid.uuid4().hex}" | ||
| 625 | + test_data = b"Some test data bytes." | ||
| 626 | + expected_size = len(test_data) | ||
| 627 | + | ||
| 628 | + writer = AsyncAppendableObjectWriter( | ||
| 629 | + grpc_client, | ||
| 630 | + _ZONAL_BUCKET, | ||
| 631 | + object_name, | ||
| 632 | + ) | ||
| 633 | + | ||
| 634 | + await writer.open() | ||
| 635 | + await writer.append(test_data) | ||
| 636 | + await writer.close(finalize_on_close=False) | ||
| 637 | + | ||
| 638 | + obj = await grpc_client.get_object( | ||
| 639 | + bucket_name=_ZONAL_BUCKET, | ||
| 640 | + object_name=object_name, | ||
| 641 | + ) | ||
| 642 | + | ||
| 643 | + # Assert | ||
| 644 | + assert obj is not None | ||
| 645 | + assert obj.name == object_name | ||
| 646 | + assert obj.bucket == f"projects/_/buckets/{_ZONAL_BUCKET}" | ||
| 647 | + assert obj.size == expected_size, ( | ||
| 648 | + f"Expected object size {expected_size}, got {obj.size}" | ||
| 649 | + ) | ||
| 650 | + | ||
| 651 | + # Cleanup | ||
| 652 | + blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name)) | ||
| 653 | + del writer | ||
| 654 | + gc.collect() | ||
| 655 | + | ||
| 656 | + event_loop.run_until_complete(_run()) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -254,3 +254,78 @@ async def test_delete_object(self, mock_async_storage_client): | |||
| 254 | 254 | assert request.if_generation_not_match == if_generation_not_match | |
| 255 | 255 | assert request.if_metageneration_match == if_metageneration_match | |
| 256 | 256 | assert request.if_metageneration_not_match == if_metageneration_not_match | |
| 257 | + | ||
| 258 | + @mock.patch("google.cloud._storage_v2.StorageAsyncClient") | ||
| 259 | + @pytest.mark.asyncio | ||
| 260 | + async def test_get_object(self, mock_async_storage_client): | ||
| 261 | + # Arrange | ||
| 262 | + mock_transport_cls = mock.MagicMock() | ||
| 263 | + mock_async_storage_client.get_transport_class.return_value = mock_transport_cls | ||
| 264 | + mock_gapic_client = mock.AsyncMock() | ||
| 265 | + mock_async_storage_client.return_value = mock_gapic_client | ||
| 266 | + | ||
| 267 | + client = async_grpc_client.AsyncGrpcClient( | ||
| 268 | + credentials=_make_credentials(spec=AnonymousCredentials) | ||
| 269 | + ) | ||
| 270 | + | ||
| 271 | + bucket_name = "bucket" | ||
| 272 | + object_name = "object" | ||
| 273 | + | ||
| 274 | + # Act | ||
| 275 | + await client.get_object( | ||
| 276 | + bucket_name, | ||
| 277 | + object_name, | ||
| 278 | + ) | ||
| 279 | + | ||
| 280 | + # Assert | ||
| 281 | + call_args, call_kwargs = mock_gapic_client.get_object.call_args | ||
| 282 | + request = call_kwargs["request"] | ||
| 283 | + assert request.bucket == "projects/_/buckets/bucket" | ||
| 284 | + assert request.object == "object" | ||
| 285 | + assert request.soft_deleted is False | ||
| 286 | + | ||
| 287 | + @mock.patch("google.cloud._storage_v2.StorageAsyncClient") | ||
| 288 | + @pytest.mark.asyncio | ||
| 289 | + async def test_get_object_with_all_parameters(self, mock_async_storage_client): | ||
| 290 | + # Arrange | ||
| 291 | + mock_transport_cls = mock.MagicMock() | ||
| 292 | + mock_async_storage_client.get_transport_class.return_value = mock_transport_cls | ||
| 293 | + mock_gapic_client = mock.AsyncMock() | ||
| 294 | + mock_async_storage_client.return_value = mock_gapic_client | ||
| 295 | + | ||
| 296 | + client = async_grpc_client.AsyncGrpcClient( | ||
| 297 | + credentials=_make_credentials(spec=AnonymousCredentials) | ||
| 298 | + ) | ||
| 299 | + | ||
| 300 | + bucket_name = "bucket" | ||
| 301 | + object_name = "object" | ||
| 302 | + generation = 123 | ||
| 303 | + if_generation_match = 456 | ||
| 304 | + if_generation_not_match = 789 | ||
| 305 | + if_metageneration_match = 111 | ||
| 306 | + if_metageneration_not_match = 222 | ||
| 307 | + soft_deleted = True | ||
| 308 | + | ||
| 309 | + # Act | ||
| 310 | + await client.get_object( | ||
| 311 | + bucket_name, | ||
| 312 | + object_name, | ||
| 313 | + generation=generation, | ||
| 314 | + if_generation_match=if_generation_match, | ||
| 315 | + if_generation_not_match=if_generation_not_match, | ||
| 316 | + if_metageneration_match=if_metageneration_match, | ||
| 317 | + if_metageneration_not_match=if_metageneration_not_match, | ||
| 318 | + soft_deleted=soft_deleted, | ||
| 319 | + ) | ||
| 320 | + | ||
| 321 | + # Assert | ||
| 322 | + call_args, call_kwargs = mock_gapic_client.get_object.call_args | ||
| 323 | + request = call_kwargs["request"] | ||
| 324 | + assert request.bucket == "projects/_/buckets/bucket" | ||
| 325 | + assert request.object == "object" | ||
| 326 | + assert request.generation == generation | ||
| 327 | + assert request.if_generation_match == if_generation_match | ||
| 328 | + assert request.if_generation_not_match == if_generation_not_match | ||
| 329 | + assert request.if_metageneration_match == if_metageneration_match | ||
| 330 | + assert request.if_metageneration_not_match == if_metageneration_not_match | ||
| 331 | + assert request.soft_deleted is True | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments