From 7583e2929b7a5c9faba656e0cb3deb2617ed9071 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 14 Apr 2021 20:11:03 +0200 Subject: [PATCH 1/7] chore(deps): update precommit hook pycqa/flake8 to v3.9.0 (#405) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9024b15d..32302e488 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,6 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.0 hooks: - id: flake8 From 9ce57e1a41182b6ec1918a23f27851c430e72101 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Fri, 16 Apr 2021 22:34:07 +0200 Subject: [PATCH 2/7] chore(deps): update precommit hook pycqa/flake8 to v3.9.1 (#411) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 32302e488..2a87c6d4d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,6 @@ repos: hooks: - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.0 + rev: 3.9.1 hooks: - id: flake8 From 412ce0baea6816e134a1ac9506065f1beb4d09c5 Mon Sep 17 00:00:00 2001 From: Dan Lee <71398022+dandhlee@users.noreply.github.com> Date: Fri, 16 Apr 2021 17:56:02 -0400 Subject: [PATCH 3/7] chore: prevent normalization of semver versioning (#410) When there is a patch version added to semver versioning, setuptools.setup(version) will normalize the versioning from `-patch` to `.patch` which is not correct SEMVER versioning. The added feature with setuptools.sic(version) will prevent this from happening. --- setup.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e1974b607..aff482545 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,20 @@ import setuptools +# Disable version normalization performed by setuptools.setup() +try: + # Try the approach of using sic(), added in setuptools 46.1.0 + from setuptools import sic +except ImportError: + # Try the approach of replacing packaging.version.Version + sic = lambda v: v + try: + # setuptools >=39.0.0 uses packaging from setuptools.extern + from setuptools.extern import packaging + except ImportError: + # setuptools <39.0.0 uses packaging from pkg_resources.extern + from pkg_resources.extern import packaging + packaging.version.Version = packaging.version.LegacyVersion # Package metadata. @@ -63,7 +77,7 @@ setuptools.setup( name=name, - version=version, + version=sic(version), description=description, long_description=readme, author="Google LLC", From f090548437142b635191e90dcee1acd4c38e565c Mon Sep 17 00:00:00 2001 From: Diego Queiroz Date: Mon, 19 Apr 2021 19:26:25 -0300 Subject: [PATCH 4/7] docs: revise docstrings for generate_signed_url (#408) * Adjusting comment for accuracy The assumed property is `signer_email` and not `service_account_email`. In nowhere a property called `service_account_email` is read from `credentials`, but `signer_email` is. * fix: adjusting comment for accuracy The assumed property is `signer_email` and not `service_account_email`. In nowhere a property called `service_account_email` is read from `credentials`, but `signer_email` is. Co-authored-by: cojenco <59401799+cojenco@users.noreply.github.com> --- google/cloud/storage/_signing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/storage/_signing.py b/google/cloud/storage/_signing.py index 16c9397e5..403df4e97 100644 --- a/google/cloud/storage/_signing.py +++ b/google/cloud/storage/_signing.py @@ -277,7 +277,7 @@ def generate_signed_url_v2( Assumes ``credentials`` implements the :class:`google.auth.credentials.Signing` interface. Also assumes - ``credentials`` has a ``service_account_email`` property which + ``credentials`` has a ``signer_email`` property which identifies the credentials. .. note:: @@ -441,7 +441,7 @@ def generate_signed_url_v4( Assumes ``credentials`` implements the :class:`google.auth.credentials.Signing` interface. Also assumes - ``credentials`` has a ``service_account_email`` property which + ``credentials`` has a ``signer_email`` property which identifies the credentials. .. note:: From 2adfb593d5ad19320affe480455568c1410b9d93 Mon Sep 17 00:00:00 2001 From: cojenco <59401799+cojenco@users.noreply.github.com> Date: Tue, 20 Apr 2021 11:33:53 -0700 Subject: [PATCH 5/7] feat: add getters and setters for encryption_key and kms_key_name (#409) * fix: add getters and setters for attributes encryption_key and kms_key_name * fix docstring formatting * revise docstring Co-authored-by: Tres Seaver --- google/cloud/storage/blob.py | 48 ++++++++++++++++++++++++++++++------ tests/unit/test_blob.py | 36 +++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/google/cloud/storage/blob.py b/google/cloud/storage/blob.py index 04212ce24..66cc1d153 100644 --- a/google/cloud/storage/blob.py +++ b/google/cloud/storage/blob.py @@ -254,6 +254,31 @@ def chunk_size(self, value): ) self._chunk_size = value + @property + def encryption_key(self): + """Retrieve the customer-supplied encryption key for the object. + + :rtype: bytes or ``NoneType`` + :returns: + The encryption key or ``None`` if no customer-supplied encryption key was used, + or the blob's resource has not been loaded from the server. + """ + return self._encryption_key + + @encryption_key.setter + def encryption_key(self, value): + """Set the blob's encryption key. + + See https://cloud.google.com/storage/docs/encryption#customer-supplied + + To perform a key rotation for an encrypted blob, use :meth:`rewrite`. + See https://cloud.google.com/storage/docs/encryption/using-customer-supplied-keys?hl=ca#rotating + + :type value: bytes + :param value: 32 byte encryption key for customer-supplied encryption. + """ + self._encryption_key = value + @staticmethod def path_helper(bucket_path, blob_name): """Relative URL path for a blob. @@ -347,25 +372,25 @@ def public_url(self): def from_string(cls, uri, client=None): """Get a constructor for blob object by URI. - :type uri: str - :param uri: The blob uri pass to get blob object. + :type uri: str + :param uri: The blob uri pass to get blob object. :type client: :class:`~google.cloud.storage.client.Client` :param client: (Optional) The client to use. If not passed, falls back to the ``client`` stored on the blob's bucket. - :rtype: :class:`google.cloud.storage.blob.Blob` - :returns: The blob object created. + :rtype: :class:`google.cloud.storage.blob.Blob` + :returns: The blob object created. - Example: - Get a constructor for blob object by URI.. + Example: + Get a constructor for blob object by URI. >>> from google.cloud import storage >>> from google.cloud.storage.blob import Blob >>> client = storage.Client() >>> blob = Blob.from_string("gs://bucket/object") - """ + """ from google.cloud.storage.bucket import Bucket scheme, netloc, path, query, frag = urlsplit(uri) @@ -3839,6 +3864,15 @@ def kms_key_name(self): """ return self._properties.get("kmsKeyName") + @kms_key_name.setter + def kms_key_name(self, value): + """Set KMS encryption key for object. + + :type value: str or ``NoneType`` + :param value: new KMS key name (None to clear any existing key). + """ + self._patch_property("kmsKeyName", value) + storage_class = _scalar_property("storageClass") """Retrieve the storage class for the object. diff --git a/tests/unit/test_blob.py b/tests/unit/test_blob.py index e8573ce21..50732a7f0 100644 --- a/tests/unit/test_blob.py +++ b/tests/unit/test_blob.py @@ -262,6 +262,42 @@ def test_acl_property(self): self.assertIsInstance(acl, ObjectACL) self.assertIs(acl, blob._acl) + def test_encryption_key_getter(self): + BLOB_NAME = "blob-name" + BUCKET = object() + blob = self._make_one(BLOB_NAME, bucket=BUCKET) + self.assertIsNone(blob.encryption_key) + VALUE = object() + blob._encryption_key = VALUE + self.assertIs(blob.encryption_key, VALUE) + + def test_encryption_key_setter(self): + BLOB_NAME = "blob-name" + BUCKET = object() + blob = self._make_one(BLOB_NAME, bucket=BUCKET) + self.assertIsNone(blob._encryption_key) + key = b"12345678901234567890123456789012" + blob.encryption_key = key + self.assertEqual(blob._encryption_key, key) + + def test_kms_key_name_getter(self): + BLOB_NAME = "blob-name" + BUCKET = object() + blob = self._make_one(BLOB_NAME, bucket=BUCKET) + self.assertIsNone(blob.kms_key_name) + VALUE = object() + blob._patch_property("kmsKeyName", VALUE) + self.assertIs(blob.kms_key_name, VALUE) + + def test_kms_key_name_setter(self): + BLOB_NAME = "blob-name" + BUCKET = object() + blob = self._make_one(BLOB_NAME, bucket=BUCKET) + self.assertIsNone(blob._properties.get("kmsKeyName")) + kms_key_name = "cryptoKeys/test-key" + blob.kms_key_name = kms_key_name + self.assertEqual(blob._properties.get("kmsKeyName"), kms_key_name) + def test_path_bad_bucket(self): fake_bucket = object() name = u"blob-name" From 23a8db839909a0781343cb18edffaea06a0b7092 Mon Sep 17 00:00:00 2001 From: Frank Natividad Date: Mon, 26 Apr 2021 12:06:19 -0700 Subject: [PATCH 6/7] fix: retry auth.TransportError errors (#418) * fix: retry auth.TransportError's * unwrap exceptions * lint * revert exceptions alias * scope unwrapping and add unit test * lint * fix unit test --- google/cloud/storage/retry.py | 17 ++++++++++------- tests/unit/test_retry.py | 11 ++++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/google/cloud/storage/retry.py b/google/cloud/storage/retry.py index e9a9eeb2f..e17f3d5a0 100644 --- a/google/cloud/storage/retry.py +++ b/google/cloud/storage/retry.py @@ -14,18 +14,19 @@ import requests -from google.api_core import exceptions +from google.api_core import exceptions as api_exceptions from google.api_core import retry +from google.auth import exceptions as auth_exceptions import json _RETRYABLE_TYPES = ( - exceptions.TooManyRequests, # 429 - exceptions.InternalServerError, # 500 - exceptions.BadGateway, # 502 - exceptions.ServiceUnavailable, # 503 - exceptions.GatewayTimeout, # 504 + api_exceptions.TooManyRequests, # 429 + api_exceptions.InternalServerError, # 500 + api_exceptions.BadGateway, # 502 + api_exceptions.ServiceUnavailable, # 503 + api_exceptions.GatewayTimeout, # 504 requests.ConnectionError, ) @@ -37,8 +38,10 @@ def _should_retry(exc): """Predicate for determining when to retry.""" if isinstance(exc, _RETRYABLE_TYPES): return True - elif isinstance(exc, exceptions.GoogleAPICallError): + elif isinstance(exc, api_exceptions.GoogleAPICallError): return exc.code in _ADDITIONAL_RETRYABLE_STATUS_CODES + elif isinstance(exc, auth_exceptions.TransportError): + return _should_retry(exc.args[0]) else: return False diff --git a/tests/unit/test_retry.py b/tests/unit/test_retry.py index d9a773cf9..582fa8097 100644 --- a/tests/unit/test_retry.py +++ b/tests/unit/test_retry.py @@ -25,7 +25,16 @@ def _call_fut(self, exc): return retry._should_retry(exc) - def test_w_retryable_types(self): + def test_w_retryable_transport_error(self): + from google.cloud.storage import retry + from google.auth.exceptions import TransportError as eTransportError + from requests import ConnectionError as rConnectionError + + caught_exc = rConnectionError("Remote end closed connection unexpected") + exc = eTransportError(caught_exc) + self.assertTrue(retry._should_retry(exc)) + + def test_w_wrapped_type(self): from google.cloud.storage import retry for exc_type in retry._RETRYABLE_TYPES: From a4805ef97fea8eba62a66e0c15ddc8f8c4a4fd3e Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 12:56:17 -0700 Subject: [PATCH 7/7] chore: release 1.38.0 (#415) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 17 +++++++++++++++++ google/cloud/storage/version.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82daf2c6d..4491c5cad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ [1]: https://pypi.org/project/google-cloud-storage/#history +## [1.38.0](https://www.github.com/googleapis/python-storage/compare/v1.37.1...v1.38.0) (2021-04-26) + + +### Features + +* add getters and setters for encryption_key and kms_key_name ([#409](https://www.github.com/googleapis/python-storage/issues/409)) ([2adfb59](https://www.github.com/googleapis/python-storage/commit/2adfb593d5ad19320affe480455568c1410b9d93)) + + +### Bug Fixes + +* retry auth.TransportError errors ([#418](https://www.github.com/googleapis/python-storage/issues/418)) ([23a8db8](https://www.github.com/googleapis/python-storage/commit/23a8db839909a0781343cb18edffaea06a0b7092)) + + +### Documentation + +* revise docstrings for generate_signed_url ([#408](https://www.github.com/googleapis/python-storage/issues/408)) ([f090548](https://www.github.com/googleapis/python-storage/commit/f090548437142b635191e90dcee1acd4c38e565c)) + ### [1.37.1](https://www.github.com/googleapis/python-storage/compare/v1.37.0...v1.37.1) (2021-04-02) diff --git a/google/cloud/storage/version.py b/google/cloud/storage/version.py index fb45c96b0..31e5a5cad 100644 --- a/google/cloud/storage/version.py +++ b/google/cloud/storage/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.37.1" +__version__ = "1.38.0"