From 2790a36891512fb6b5dea901faad728396dd4121 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 9 Aug 2018 15:35:00 -0500 Subject: [PATCH 1/4] support deleting a specific blob generation --- storage/google/cloud/storage/blob.py | 4 ++-- storage/google/cloud/storage/bucket.py | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py index 27fcc3996c02..0de60d3adeee 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 @@ -405,7 +405,7 @@ def delete(self, client=None): (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..4293b5f58968 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,11 @@ 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: :class:`~google.cloud.storage.client.Client` 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 +621,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. From 9cb978651ac29cae6ccb61e1b42b309e9c4fe6b5 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 9 Aug 2018 15:39:47 -0500 Subject: [PATCH 2/4] add ability to delete a specific blob generation --- storage/google/cloud/storage/blob.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py index 0de60d3adeee..ab9d1949ea67 100644 --- a/storage/google/cloud/storage/blob.py +++ b/storage/google/cloud/storage/blob.py @@ -399,6 +399,11 @@ def delete(self, client=None, generation=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the blob's bucket. + :type generation: :class:`~google.cloud.storage.client.Client` 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` From e84fe39dff7230a0584dfe87e874d812a2bada52 Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 9 Aug 2018 15:45:42 -0500 Subject: [PATCH 3/4] add test for deleting blob with generation --- storage/tests/unit/test_blob.py | 14 +++++++++++++- storage/tests/unit/test_bucket.py | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) 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) From bcd7b8d369d6d09c3dd0adf77ced355611cfd8bf Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Thu, 9 Aug 2018 16:34:37 -0500 Subject: [PATCH 4/4] fix docstrings --- storage/google/cloud/storage/blob.py | 3 +-- storage/google/cloud/storage/bucket.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py index ab9d1949ea67..c4eb0ec7ee1f 100644 --- a/storage/google/cloud/storage/blob.py +++ b/storage/google/cloud/storage/blob.py @@ -399,8 +399,7 @@ def delete(self, client=None, generation=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the blob's bucket. - :type generation: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` + :type generation: int or ``NoneType`` :param generation: Optional. The generation of the blob to delete. If not passed, deletes the current version. diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py index 4293b5f58968..4c89cbac399d 100644 --- a/storage/google/cloud/storage/bucket.py +++ b/storage/google/cloud/storage/bucket.py @@ -597,8 +597,7 @@ def delete_blob(self, blob_name, client=None, generation=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the current bucket. - :type generation: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` + :type generation: int or ``NoneType`` :param generation: Optional. The generation of the blob to delete. If not passed, deletes the current version.