Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 4878836

Browse files
committed
refactor: use 'Client._delete_resource' in 'Bucket.delete'
Also, forward 'retry' through when deleting blobs during 'Bucket.delete'. Toward #38.
1 parent f36b4f9 commit 4878836

2 files changed

Lines changed: 175 additions & 131 deletions

File tree

google/cloud/storage/bucket.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,9 +1466,9 @@ def delete(
14661466
self,
14671467
force=False,
14681468
client=None,
1469-
timeout=_DEFAULT_TIMEOUT,
14701469
if_metageneration_match=None,
14711470
if_metageneration_not_match=None,
1471+
timeout=_DEFAULT_TIMEOUT,
14721472
retry=DEFAULT_RETRY,
14731473
):
14741474
"""Delete this bucket.
@@ -1496,13 +1496,6 @@ def delete(
14961496
:param client: (Optional) The client to use. If not passed, falls back
14971497
to the ``client`` stored on the current bucket.
14981498
1499-
:type timeout: float or tuple
1500-
:param timeout: (Optional) The amount of time, in seconds, to wait
1501-
for the server response on each request.
1502-
1503-
Can also be passed as a tuple (connect_timeout, read_timeout).
1504-
See :meth:`requests.Session.request` documentation for details.
1505-
15061499
:type if_metageneration_match: long
15071500
:param if_metageneration_match: (Optional) Make the operation conditional on whether the
15081501
blob's current metageneration matches the given value.
@@ -1511,6 +1504,13 @@ def delete(
15111504
:param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
15121505
blob's current metageneration does not match the given value.
15131506
1507+
:type timeout: float or tuple
1508+
:param timeout: (Optional) The amount of time, in seconds, to wait
1509+
for the server response on each request.
1510+
1511+
Can also be passed as a tuple (connect_timeout, read_timeout).
1512+
See :meth:`requests.Session.request` documentation for details.
1513+
15141514
:type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
15151515
:param retry: (Optional) How to retry the RPC. A None value will disable retries.
15161516
A google.api_core.retry.Retry value will enable retries, and the object will
@@ -1545,6 +1545,7 @@ def delete(
15451545
max_results=self._MAX_OBJECTS_FOR_ITERATION + 1,
15461546
client=client,
15471547
timeout=timeout,
1548+
retry=retry,
15481549
)
15491550
)
15501551
if len(blobs) > self._MAX_OBJECTS_FOR_ITERATION:
@@ -1558,19 +1559,22 @@ def delete(
15581559

15591560
# Ignore 404 errors on delete.
15601561
self.delete_blobs(
1561-
blobs, on_error=lambda blob: None, client=client, timeout=timeout
1562+
blobs,
1563+
on_error=lambda blob: None,
1564+
client=client,
1565+
timeout=timeout,
1566+
retry=retry,
15621567
)
15631568

15641569
# We intentionally pass `_target_object=None` since a DELETE
15651570
# request has no response value (whether in a standard request or
15661571
# in a batch request).
1567-
client._connection.api_request(
1568-
method="DELETE",
1569-
path=self.path,
1572+
client._delete_resource(
1573+
self.path,
15701574
query_params=query_params,
1571-
_target_object=None,
15721575
timeout=timeout,
15731576
retry=retry,
1577+
_target_object=None,
15741578
)
15751579

15761580
def delete_blob(

tests/unit/test_bucket.py

Lines changed: 158 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,139 +1107,179 @@ def test_get_notification_hit_w_explicit_w_user_project(self):
11071107
retry=retry,
11081108
)
11091109

1110-
def test_delete_miss(self):
1110+
def test_delete_miss_w_defaults(self):
11111111
from google.cloud.exceptions import NotFound
11121112

1113-
NAME = "name"
1114-
connection = _Connection()
1115-
client = _Client(connection)
1116-
bucket = self._make_one(client=client, name=NAME)
1117-
self.assertRaises(NotFound, bucket.delete)
1118-
expected_cw = [
1119-
{
1120-
"method": "DELETE",
1121-
"path": bucket.path,
1122-
"query_params": {},
1123-
"_target_object": None,
1124-
"timeout": self._get_default_timeout(),
1125-
"retry": DEFAULT_RETRY,
1126-
}
1127-
]
1128-
self.assertEqual(connection._deleted_buckets, expected_cw)
1113+
name = "name"
1114+
client = mock.Mock(spec=["_delete_resource"])
1115+
client._delete_resource.side_effect = NotFound("testing")
1116+
bucket = self._make_one(client=client, name=name)
1117+
1118+
with self.assertRaises(NotFound):
1119+
bucket.delete()
1120+
1121+
expected_query_params = {}
1122+
client._delete_resource.assert_called_once_with(
1123+
bucket.path,
1124+
query_params=expected_query_params,
1125+
timeout=self._get_default_timeout(),
1126+
retry=DEFAULT_RETRY,
1127+
_target_object=None,
1128+
)
1129+
1130+
def test_delete_hit_w_metageneration_match_w_explicit_client(self):
1131+
name = "name"
1132+
metageneration_number = 6
1133+
client = mock.Mock(spec=["_delete_resource"])
1134+
client._delete_resource.return_value = None
1135+
bucket = self._make_one(client=None, name=name)
1136+
1137+
result = bucket.delete(
1138+
client=client, if_metageneration_match=metageneration_number,
1139+
)
11291140

1130-
def test_delete_hit_with_user_project(self):
1131-
NAME = "name"
1132-
USER_PROJECT = "user-project-123"
1133-
GET_BLOBS_RESP = {"items": []}
1134-
connection = _Connection(GET_BLOBS_RESP)
1135-
connection._delete_bucket = True
1136-
client = self._make_client()
1137-
client._base_connection = connection
1138-
bucket = self._make_one(client=client, name=NAME, user_project=USER_PROJECT)
1139-
result = bucket.delete(force=True, timeout=42)
11401141
self.assertIsNone(result)
1141-
expected_cw = [
1142-
{
1143-
"method": "DELETE",
1144-
"path": bucket.path,
1145-
"_target_object": None,
1146-
"query_params": {"userProject": USER_PROJECT},
1147-
"timeout": 42,
1148-
"retry": DEFAULT_RETRY,
1149-
}
1150-
]
1151-
self.assertEqual(connection._deleted_buckets, expected_cw)
11521142

1153-
def test_delete_force_delete_blobs(self):
1154-
NAME = "name"
1155-
BLOB_NAME1 = "blob-name1"
1156-
BLOB_NAME2 = "blob-name2"
1157-
GET_BLOBS_RESP = {"items": [{"name": BLOB_NAME1}, {"name": BLOB_NAME2}]}
1158-
DELETE_BLOB1_RESP = DELETE_BLOB2_RESP = {}
1159-
connection = _Connection(GET_BLOBS_RESP, DELETE_BLOB1_RESP, DELETE_BLOB2_RESP)
1160-
connection._delete_bucket = True
1161-
client = self._make_client()
1162-
client._base_connection = connection
1163-
bucket = self._make_one(client=client, name=NAME)
1164-
result = bucket.delete(force=True)
1143+
expected_query_params = {"ifMetagenerationMatch": metageneration_number}
1144+
client._delete_resource.assert_called_once_with(
1145+
bucket.path,
1146+
query_params=expected_query_params,
1147+
timeout=self._get_default_timeout(),
1148+
retry=DEFAULT_RETRY,
1149+
_target_object=None,
1150+
)
1151+
1152+
def test_delete_hit_w_force_w_user_project_w_explicit_timeout_retry(self):
1153+
name = "name"
1154+
user_project = "user-project-123"
1155+
client = mock.Mock(spec=["_delete_resource"])
1156+
client._delete_resource.return_value = None
1157+
bucket = self._make_one(client=client, name=name, user_project=user_project)
1158+
bucket.list_blobs = mock.Mock(return_value=iter([]))
1159+
bucket.delete_blobs = mock.Mock(return_value=None)
1160+
timeout = 42
1161+
retry = mock.Mock(spec=[])
1162+
1163+
result = bucket.delete(force=True, timeout=timeout, retry=retry)
1164+
11651165
self.assertIsNone(result)
1166-
expected_cw = [
1167-
{
1168-
"method": "DELETE",
1169-
"path": bucket.path,
1170-
"query_params": {},
1171-
"_target_object": None,
1172-
"timeout": self._get_default_timeout(),
1173-
"retry": DEFAULT_RETRY,
1174-
}
1175-
]
1176-
self.assertEqual(connection._deleted_buckets, expected_cw)
11771166

1178-
def test_delete_with_metageneration_match(self):
1179-
NAME = "name"
1180-
BLOB_NAME1 = "blob-name1"
1181-
BLOB_NAME2 = "blob-name2"
1182-
GET_BLOBS_RESP = {"items": [{"name": BLOB_NAME1}, {"name": BLOB_NAME2}]}
1183-
DELETE_BLOB1_RESP = DELETE_BLOB2_RESP = {}
1184-
METAGENERATION_NUMBER = 6
1185-
1186-
connection = _Connection(GET_BLOBS_RESP, DELETE_BLOB1_RESP, DELETE_BLOB2_RESP)
1187-
connection._delete_bucket = True
1188-
client = _Client(connection)
1189-
bucket = self._make_one(client=client, name=NAME)
1190-
result = bucket.delete(if_metageneration_match=METAGENERATION_NUMBER)
1167+
bucket.list_blobs.assert_called_once_with(
1168+
max_results=bucket._MAX_OBJECTS_FOR_ITERATION + 1,
1169+
client=client,
1170+
timeout=timeout,
1171+
retry=retry,
1172+
)
1173+
1174+
bucket.delete_blobs.assert_called_once_with(
1175+
[], on_error=mock.ANY, client=client, timeout=timeout, retry=retry,
1176+
)
1177+
1178+
expected_query_params = {"userProject": user_project}
1179+
client._delete_resource.assert_called_once_with(
1180+
bucket.path,
1181+
query_params=expected_query_params,
1182+
timeout=timeout,
1183+
retry=retry,
1184+
_target_object=None,
1185+
)
1186+
1187+
def test_delete_hit_w_force_delete_blobs(self):
1188+
name = "name"
1189+
client = mock.Mock(spec=["_delete_resource"])
1190+
client._delete_resource.return_value = None
1191+
bucket = self._make_one(client=client, name=name)
1192+
blobs = [mock.Mock(spec=[]), mock.Mock(spec=[])]
1193+
bucket.list_blobs = mock.Mock(return_value=iter(blobs))
1194+
bucket.delete_blobs = mock.Mock(return_value=None)
1195+
1196+
result = bucket.delete(force=True)
1197+
11911198
self.assertIsNone(result)
1192-
expected_cw = [
1193-
{
1194-
"method": "DELETE",
1195-
"path": bucket.path,
1196-
"query_params": {"ifMetagenerationMatch": METAGENERATION_NUMBER},
1197-
"_target_object": None,
1198-
"timeout": self._get_default_timeout(),
1199-
"retry": DEFAULT_RETRY,
1200-
}
1201-
]
1202-
self.assertEqual(connection._deleted_buckets, expected_cw)
12031199

1204-
def test_delete_force_miss_blobs(self):
1205-
NAME = "name"
1206-
BLOB_NAME = "blob-name1"
1207-
GET_BLOBS_RESP = {"items": [{"name": BLOB_NAME}]}
1208-
# Note the connection does not have a response for the blob.
1209-
connection = _Connection(GET_BLOBS_RESP)
1210-
connection._delete_bucket = True
1211-
client = self._make_client()
1212-
client._base_connection = connection
1213-
bucket = self._make_one(client=client, name=NAME)
1200+
bucket.list_blobs.assert_called_once_with(
1201+
max_results=bucket._MAX_OBJECTS_FOR_ITERATION + 1,
1202+
client=client,
1203+
timeout=self._get_default_timeout(),
1204+
retry=DEFAULT_RETRY,
1205+
)
1206+
1207+
bucket.delete_blobs.assert_called_once_with(
1208+
blobs,
1209+
on_error=mock.ANY,
1210+
client=client,
1211+
timeout=self._get_default_timeout(),
1212+
retry=DEFAULT_RETRY,
1213+
)
1214+
1215+
expected_query_params = {}
1216+
client._delete_resource.assert_called_once_with(
1217+
bucket.path,
1218+
query_params=expected_query_params,
1219+
timeout=self._get_default_timeout(),
1220+
retry=DEFAULT_RETRY,
1221+
_target_object=None,
1222+
)
1223+
1224+
def test_delete_w_force_w_user_project_w_miss_on_blob(self):
1225+
from google.cloud.exceptions import NotFound
1226+
1227+
name = "name"
1228+
blob_name = "blob-name"
1229+
client = mock.Mock(spec=["_delete_resource"])
1230+
client._delete_resource.return_value = None
1231+
bucket = self._make_one(client=client, name=name)
1232+
blob = mock.Mock(spec=["name"])
1233+
blob.name = blob_name
1234+
blobs = [blob]
1235+
bucket.list_blobs = mock.Mock(return_value=iter(blobs))
1236+
bucket.delete_blob = mock.Mock(side_effect=NotFound("testing"))
1237+
12141238
result = bucket.delete(force=True)
1239+
12151240
self.assertIsNone(result)
1216-
expected_cw = [
1217-
{
1218-
"method": "DELETE",
1219-
"path": bucket.path,
1220-
"query_params": {},
1221-
"_target_object": None,
1222-
"timeout": self._get_default_timeout(),
1223-
"retry": DEFAULT_RETRY,
1224-
}
1225-
]
1226-
self.assertEqual(connection._deleted_buckets, expected_cw)
12271241

1228-
def test_delete_too_many(self):
1229-
NAME = "name"
1230-
BLOB_NAME1 = "blob-name1"
1231-
BLOB_NAME2 = "blob-name2"
1232-
GET_BLOBS_RESP = {"items": [{"name": BLOB_NAME1}, {"name": BLOB_NAME2}]}
1233-
connection = _Connection(GET_BLOBS_RESP)
1234-
connection._delete_bucket = True
1235-
client = self._make_client()
1236-
client._base_connection = connection
1237-
bucket = self._make_one(client=client, name=NAME)
1242+
bucket.delete_blob.assert_called_once_with(
1243+
blob_name,
1244+
client=client,
1245+
if_generation_match=None,
1246+
if_generation_not_match=None,
1247+
if_metageneration_match=None,
1248+
if_metageneration_not_match=None,
1249+
timeout=self._get_default_timeout(),
1250+
retry=DEFAULT_RETRY,
1251+
)
12381252

1253+
expected_query_params = {}
1254+
client._delete_resource.assert_called_once_with(
1255+
bucket.path,
1256+
query_params=expected_query_params,
1257+
timeout=self._get_default_timeout(),
1258+
retry=DEFAULT_RETRY,
1259+
_target_object=None,
1260+
)
1261+
1262+
def test_delete_w_too_many(self):
1263+
name = "name"
1264+
blob_name1 = "blob-name1"
1265+
blob_name2 = "blob-name2"
1266+
client = mock.Mock(spec=["_delete_resource"])
1267+
client._delete_resource.return_value = None
1268+
bucket = self._make_one(client=client, name=name)
1269+
blob1 = mock.Mock(spec=["name"])
1270+
blob1.name = blob_name1
1271+
blob2 = mock.Mock(spec=["name"])
1272+
blob2.name = blob_name2
1273+
blobs = [blob1, blob2]
1274+
bucket.list_blobs = mock.Mock(return_value=iter(blobs))
1275+
bucket.delete_blobs = mock.Mock()
12391276
# Make the Bucket refuse to delete with 2 objects.
12401277
bucket._MAX_OBJECTS_FOR_ITERATION = 1
1241-
self.assertRaises(ValueError, bucket.delete, force=True)
1242-
self.assertEqual(connection._deleted_buckets, [])
1278+
1279+
with self.assertRaises(ValueError):
1280+
bucket.delete(force=True)
1281+
1282+
bucket.delete_blobs.assert_not_called()
12431283

12441284
def test_delete_blob_miss_w_defaults(self):
12451285
from google.cloud.exceptions import NotFound

0 commit comments

Comments
 (0)