| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -106,3 +106,55 @@ def grpc_client(self): | |||
| 106 | 106 | google.cloud._storage_v2.StorageAsyncClient: The configured GAPIC client. | |
| 107 | 107 | """ | |
| 108 | 108 | return self._grpc_client | |
| 109 | + | ||
| 110 | + async def delete_object( | ||
| 111 | + self, | ||
| 112 | + bucket_name, | ||
| 113 | + object_name, | ||
| 114 | + generation=None, | ||
| 115 | + if_generation_match=None, | ||
| 116 | + if_generation_not_match=None, | ||
| 117 | + if_metageneration_match=None, | ||
| 118 | + if_metageneration_not_match=None, | ||
| 119 | + **kwargs, | ||
| 120 | + ): | ||
| 121 | + """Deletes an object and its metadata. | ||
| 122 | + | ||
| 123 | + :type bucket_name: str | ||
| 124 | + :param bucket_name: The name of the bucket in which the object resides. | ||
| 125 | + | ||
| 126 | + :type object_name: str | ||
| 127 | + :param object_name: The name of the object to delete. | ||
| 128 | + | ||
| 129 | + :type generation: int | ||
| 130 | + :param generation: | ||
| 131 | + (Optional) If present, permanently deletes a specific generation | ||
| 132 | + of an object. | ||
| 133 | + | ||
| 134 | + :type if_generation_match: int | ||
| 135 | + :param if_generation_match: (Optional) | ||
| 136 | + | ||
| 137 | + :type if_generation_not_match: int | ||
| 138 | + :param if_generation_not_match: (Optional) | ||
| 139 | + | ||
| 140 | + :type if_metageneration_match: int | ||
| 141 | + :param if_metageneration_match: (Optional) | ||
| 142 | + | ||
| 143 | + :type if_metageneration_not_match: int | ||
| 144 | + :param if_metageneration_not_match: (Optional) | ||
| 145 | + | ||
| 146 | + | ||
| 147 | + """ | ||
| 148 | + # The gRPC API requires the bucket name to be in the format "projects/_/buckets/bucket_name" | ||
| 149 | + bucket_path = f"projects/_/buckets/{bucket_name}" | ||
| 150 | + request = storage_v2.DeleteObjectRequest( | ||
| 151 | + bucket=bucket_path, | ||
| 152 | + object=object_name, | ||
| 153 | + generation=generation, | ||
| 154 | + if_generation_match=if_generation_match, | ||
| 155 | + if_generation_not_match=if_generation_not_match, | ||
| 156 | + if_metageneration_match=if_metageneration_match, | ||
| 157 | + if_metageneration_not_match=if_metageneration_not_match, | ||
| 158 | + **kwargs, | ||
| 159 | + ) | ||
| 160 | + await self._grpc_client.delete_object(request=request) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,7 +19,7 @@ | |||
| 19 | 19 | from google.cloud.storage.asyncio.async_multi_range_downloader import ( | |
| 20 | 20 | AsyncMultiRangeDownloader, | |
| 21 | 21 | ) | |
| 22 | - from google.api_core.exceptions import FailedPrecondition | ||
| 22 | + from google.api_core.exceptions import FailedPrecondition, NotFound | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | pytestmark = pytest.mark.skipif( | |
@@ -570,3 +570,34 @@ async def _run(): | |||
| 570 | 570 | blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name)) | |
| 571 | 571 | ||
| 572 | 572 | event_loop.run_until_complete(_run()) | |
| 573 | + | ||
| 574 | + | ||
| 575 | + def test_delete_object_using_grpc_client(event_loop, grpc_client_direct): | ||
| 576 | + """ | ||
| 577 | + Test that a new writer when specifies `None` overrides the existing object. | ||
| 578 | + """ | ||
| 579 | + object_name = f"test_append_with_generation-{uuid.uuid4()}" | ||
| 580 | + | ||
| 581 | + async def _run(): | ||
| 582 | + writer = AsyncAppendableObjectWriter( | ||
| 583 | + grpc_client_direct, _ZONAL_BUCKET, object_name, generation=0 | ||
| 584 | + ) | ||
| 585 | + | ||
| 586 | + # Empty object is created. | ||
| 587 | + await writer.open() | ||
| 588 | + await writer.append(b"some_bytes") | ||
| 589 | + await writer.close() | ||
| 590 | + | ||
| 591 | + await grpc_client_direct.delete_object(_ZONAL_BUCKET, object_name) | ||
| 592 | + | ||
| 593 | + # trying to get raises raises 404. | ||
| 594 | + with pytest.raises(NotFound): | ||
| 595 | + # TODO: Remove this once GET_OBJECT is exposed in `AsyncGrpcClient` | ||
| 596 | + await grpc_client_direct._grpc_client.get_object( | ||
| 597 | + bucket=f"projects/_/buckets/{_ZONAL_BUCKET}", object_=object_name | ||
| 598 | + ) | ||
| 599 | + # cleanup | ||
| 600 | + del writer | ||
| 601 | + gc.collect() | ||
| 602 | + | ||
| 603 | + event_loop.run_until_complete(_run()) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,6 +13,7 @@ | |||
| 13 | 13 | # limitations under the License. | |
| 14 | 14 | ||
| 15 | 15 | from unittest import mock | |
| 16 | + import pytest | ||
| 16 | 17 | from google.auth import credentials as auth_credentials | |
| 17 | 18 | from google.auth.credentials import AnonymousCredentials | |
| 18 | 19 | from google.api_core import client_info as client_info_lib | |
@@ -185,6 +186,7 @@ def test_grpc_client_with_anon_creds(self, mock_grpc_gapic_client): | |||
| 185 | 186 | credentials=anonymous_creds, | |
| 186 | 187 | options=expected_options, | |
| 187 | 188 | ) | |
| 189 | + mock_transport_cls.assert_called_once_with(channel=channel_sentinel) | ||
| 188 | 190 | ||
| 189 | 191 | @mock.patch("google.cloud._storage_v2.StorageAsyncClient") | |
| 190 | 192 | def test_user_agent_with_custom_client_info(self, mock_async_storage_client): | |
@@ -209,3 +211,46 @@ def test_user_agent_with_custom_client_info(self, mock_async_storage_client): | |||
| 209 | 211 | agent_version = f"gcloud-python/{__version__}" | |
| 210 | 212 | expected_user_agent = f"custom-app/1.0 {agent_version} " | |
| 211 | 213 | assert client_info.user_agent == expected_user_agent | |
| 214 | + | ||
| 215 | + @mock.patch("google.cloud._storage_v2.StorageAsyncClient") | ||
| 216 | + @pytest.mark.asyncio | ||
| 217 | + async def test_delete_object(self, mock_async_storage_client): | ||
| 218 | + # Arrange | ||
| 219 | + mock_transport_cls = mock.MagicMock() | ||
| 220 | + mock_async_storage_client.get_transport_class.return_value = mock_transport_cls | ||
| 221 | + mock_gapic_client = mock.AsyncMock() | ||
| 222 | + mock_async_storage_client.return_value = mock_gapic_client | ||
| 223 | + | ||
| 224 | + client = async_grpc_client.AsyncGrpcClient( | ||
| 225 | + credentials=_make_credentials(spec=AnonymousCredentials) | ||
| 226 | + ) | ||
| 227 | + | ||
| 228 | + bucket_name = "bucket" | ||
| 229 | + object_name = "object" | ||
| 230 | + generation = 123 | ||
| 231 | + if_generation_match = 456 | ||
| 232 | + if_generation_not_match = 789 | ||
| 233 | + if_metageneration_match = 111 | ||
| 234 | + if_metageneration_not_match = 222 | ||
| 235 | + | ||
| 236 | + # Act | ||
| 237 | + await client.delete_object( | ||
| 238 | + bucket_name, | ||
| 239 | + object_name, | ||
| 240 | + generation=generation, | ||
| 241 | + if_generation_match=if_generation_match, | ||
| 242 | + if_generation_not_match=if_generation_not_match, | ||
| 243 | + if_metageneration_match=if_metageneration_match, | ||
| 244 | + if_metageneration_not_match=if_metageneration_not_match, | ||
| 245 | + ) | ||
| 246 | + | ||
| 247 | + # Assert | ||
| 248 | + call_args, call_kwargs = mock_gapic_client.delete_object.call_args | ||
| 249 | + request = call_kwargs["request"] | ||
| 250 | + assert request.bucket == "projects/_/buckets/bucket" | ||
| 251 | + assert request.object == "object" | ||
| 252 | + assert request.generation == generation | ||
| 253 | + assert request.if_generation_match == if_generation_match | ||
| 254 | + assert request.if_generation_not_match == if_generation_not_match | ||
| 255 | + assert request.if_metageneration_match == if_metageneration_match | ||
| 256 | + assert request.if_metageneration_not_match == if_metageneration_not_match | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments