Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions google/cloud/storage/_experimental/asyncio/async_grpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AsyncGrpcClient:
(Optional) Whether to attempt to use DirectPath for gRPC connections.
Defaults to ``True``.
"""

def __init__(
self,
credentials=None,
Expand Down Expand Up @@ -97,3 +98,83 @@ def grpc_client(self):
google.cloud._storage_v2.StorageAsyncClient: The configured GAPIC client.
"""
return self._grpc_client

async def delete_object(
self,
bucket_name,
object_name,
generation=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
**kwargs,
):
"""Deletes an object and its metadata.

:type bucket_name: str
:param bucket_name: The name of the bucket in which the object resides.

:type object_name: str
:param object_name: The name of the object to delete.

:type generation: int
:param generation:
(Optional) If present, permanently deletes a specific generation
of an object.

:type if_generation_match: long
Comment thread
chandra-siri marked this conversation as resolved.
Outdated
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`

:type if_generation_not_match: long
Comment thread
chandra-siri marked this conversation as resolved.
Outdated
:param if_generation_not_match:
(Optional) See :ref:`using-if-generation-not-match`

:type if_metageneration_match: long
Comment thread
chandra-siri marked this conversation as resolved.
Outdated
:param if_metageneration_match:
(Optional) See :ref:`using-if-metageneration-match`

:type if_metageneration_not_match: long
Comment thread
chandra-siri marked this conversation as resolved.
Outdated
:param if_metageneration_not_match:
(Optional) See :ref:`using-if-metageneration-not-match`


"""
# The gRPC API requires the bucket name to be in the format "projects/_/buckets/bucket_name"
bucket_path = f"projects/_/buckets/{bucket_name}"
request = storage_v2.DeleteObjectRequest(
bucket=bucket_path, object=object_name, generation=generation,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
**kwargs,
)
await self._grpc_client.delete_object(request=request)


if __name__ == "__main__":
import asyncio

# This is a sample showing how to use the delete_object method.
# To run this sample, install the library and set up authentication.
# See google-cloud-storage documentation for more details.
#
# You will need to replace `your-bucket-name` and `your-object-name`
# with your actual bucket name and object name.

BUCKET_NAME = "chandrasiri-benchmarks-zb"
OBJECT_NAME = "demo-1GiB-100-objects-298"

async def main():
client = AsyncGrpcClient()
print(f"Attempting to delete {OBJECT_NAME} from bucket {BUCKET_NAME}...")
try:
await client.delete_object(BUCKET_NAME, OBJECT_NAME, if_generation_match=1768326969788933)
print("Object deleted successfully.")
except Exception as e:
print(f"An error occurred: {e}")
raise
Comment thread
chandra-siri marked this conversation as resolved.
Outdated

asyncio.run(main())
32 changes: 31 additions & 1 deletion tests/system/test_zonal.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from google.cloud.storage._experimental.asyncio.async_multi_range_downloader import (
AsyncMultiRangeDownloader,
)
from google.api_core.exceptions import FailedPrecondition
from google.api_core.exceptions import FailedPrecondition, NotFound


pytestmark = pytest.mark.skipif(
Expand Down Expand Up @@ -570,3 +570,33 @@ async def _run():
blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name))

event_loop.run_until_complete(_run())

def test_delete_object_using_grpc_client(
event_loop, grpc_client
):
"""
Test that a new writer when specifies `None` overrides the existing object.
Comment on lines +576 to +577
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The docstring for test_delete_object_using_grpc_client is incorrect. It describes a test for overriding an existing object, not for deleting an object. Please update it to accurately reflect the purpose of this test.

Suggested change
"""
Test that a new writer when specifies `None` overrides the existing object.
"""
Test that `delete_object` using the gRPC client successfully deletes an object.
"""

"""
object_name = f"test_append_with_generation-{uuid.uuid4()}"

async def _run():
writer = AsyncAppendableObjectWriter(
grpc_client, _ZONAL_BUCKET, object_name, generation=0
)

# Empty object is created.
await writer.open()
await writer.append(b'some_bytes')
await writer.close()

await grpc_client.delete_object(bucket=f"projects/_/buckets/{_ZONAL_BUCKET}", object_=object_name)
Comment thread
chandra-siri marked this conversation as resolved.
Outdated


# trying to get raises raises 404.
with pytest.raises(NotFound):
await grpc_client.get_object(bucket=f"projects/_/buckets/{_ZONAL_BUCKET}", object_=object_name)
Comment thread
chandra-siri marked this conversation as resolved.
Outdated
# cleanup
del writer
gc.collect()

event_loop.run_until_complete(_run())
43 changes: 43 additions & 0 deletions tests/unit/asyncio/test_async_grpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,46 @@ def test_grpc_client_with_anon_creds(self, mock_grpc_gapic_client):
options=expected_options,
)
mock_transport_cls.assert_called_once_with(channel=channel_sentinel)


# TODO(developer): Add unit tests for all the methods in async_grpc_client.py
class TestDeleteObject(unittest.IsolatedAsyncioTestCase):
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
async def test_delete_object(self, mock_async_storage_client):
# Arrange
client = async_grpc_client.AsyncGrpcClient(
credentials=_make_credentials(spec=AnonymousCredentials)
)
client._grpc_client = mock.AsyncMock()

bucket_name = "bucket"
object_name = "object"
generation = 123
if_generation_match = 456
if_generation_not_match = 789
if_metageneration_match = 111
if_metageneration_not_match = 222

# Act
await client.delete_object(
bucket_name,
object_name,
generation=generation,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
)

# Assert
call_args, call_kwargs = client._grpc_client.delete_object.call_args
request = call_kwargs["request"]
self.assertEqual(request.bucket, "projects/_/buckets/bucket")
self.assertEqual(request.object, "object")
self.assertEqual(request.generation, generation)
self.assertEqual(request.if_generation_match, if_generation_match)
self.assertEqual(request.if_generation_not_match, if_generation_not_match)
self.assertEqual(request.if_metageneration_match, if_metageneration_match)
self.assertEqual(
request.if_metageneration_not_match, if_metageneration_not_match
)