diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py index 27fcc3996c02..c4eb0ec7ee1f 100644 --- a/storage/google/cloud/storage/blob.py +++ b/storage/google/cloud/storage/blob.py @@ -388,7 +388,7 @@ def exists(self, client=None): except NotFound: return False - def delete(self, client=None): + def delete(self, client=None, generation=None): """Deletes a blob from Cloud Storage. If :attr:`user_project` is set on the bucket, bills the API request @@ -399,13 +399,17 @@ def delete(self, client=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the blob's bucket. + :type generation: int or ``NoneType`` + :param generation: Optional. The generation of the blob to delete. If + not passed, deletes the current version. + :rtype: :class:`Blob` :returns: The blob that was just deleted. :raises: :class:`google.cloud.exceptions.NotFound` (propagated from :meth:`google.cloud.storage.bucket.Bucket.delete_blob`). """ - return self.bucket.delete_blob(self.name, client=client) + return self.bucket.delete_blob(self.name, client=client, generation=generation) def _get_transport(self, client): """Return the client's transport. diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py index 43640b789dbc..4c89cbac399d 100644 --- a/storage/google/cloud/storage/bucket.py +++ b/storage/google/cloud/storage/bucket.py @@ -575,7 +575,7 @@ def delete(self, force=False, client=None): query_params=query_params, _target_object=None) - def delete_blob(self, blob_name, client=None): + def delete_blob(self, blob_name, client=None, generation=None): """Deletes a blob from the current bucket. If the blob isn't found (backend 404), raises a @@ -597,6 +597,10 @@ def delete_blob(self, blob_name, client=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the current bucket. + :type generation: int or ``NoneType`` + :param generation: Optional. The generation of the blob to delete. If + not passed, deletes the current version. + :raises: :class:`google.cloud.exceptions.NotFound` (to suppress the exception, call ``delete_blobs``, passing a no-op ``on_error`` callback, e.g.: @@ -616,11 +620,18 @@ def delete_blob(self, blob_name, client=None): # We intentionally pass `_target_object=None` since a DELETE # request has no response value (whether in a standard request or # in a batch request). - client._connection.api_request( - method='DELETE', - path=blob_path, - query_params=query_params, - _target_object=None) + if not generation: + client._connection.api_request( + method='DELETE', + path=blob_path, + query_params=query_params, + _target_object=None) + else: + client._connection.api_request( + method='DELETE', + path=blob_path + '?generation={}'.format(generation), + query_params=query_params, + _target_object=None) def delete_blobs(self, blobs, on_error=None, client=None): """Deletes a list of blobs from the current bucket. diff --git a/storage/tests/unit/test_blob.py b/storage/tests/unit/test_blob.py index 28d1c4160b1e..286cec8d1c93 100644 --- a/storage/tests/unit/test_blob.py +++ b/storage/tests/unit/test_blob.py @@ -543,6 +543,18 @@ def test_delete(self): self.assertFalse(blob.exists()) self.assertEqual(bucket._deleted, [(BLOB_NAME, None)]) + def test_delete_with_generation(self): + BLOB_NAME = 'blob-name' + not_found_response = ({'status': http_client.NOT_FOUND}, b'') + connection = _Connection(not_found_response) + client = _Client(connection) + bucket = _Bucket(client) + blob = self._make_one(BLOB_NAME, bucket=bucket) + bucket._blobs[BLOB_NAME] = 1 + blob.delete(generation=blob.generation) + self.assertFalse(blob.exists()) + self.assertEqual(bucket._deleted, [(BLOB_NAME, None)]) + def test__get_transport(self): client = mock.Mock(spec=[u'_credentials', '_http']) client._http = mock.sentinel.transport @@ -3027,7 +3039,7 @@ def __init__(self, client=None, name='name', user_project=None): self.path = '/b/' + name self.user_project = user_project - def delete_blob(self, blob_name, client=None): + def delete_blob(self, blob_name, client=None, generation=None): del self._blobs[blob_name] self._deleted.append((blob_name, client)) diff --git a/storage/tests/unit/test_bucket.py b/storage/tests/unit/test_bucket.py index 1ce3522d1290..c3b3be6d36b4 100644 --- a/storage/tests/unit/test_bucket.py +++ b/storage/tests/unit/test_bucket.py @@ -874,7 +874,7 @@ def __init__(self, name, bucket_name): self.path = '/b/%s/o/%s' % (bucket_name, name) self._deleted = [] - def delete(self, client=None): + def delete(self, client=None, generation=None): self._deleted.append(client) blob = _Blob(BLOB_NAME, BUCKET_NAME)