Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/.OwlBot.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# limitations under the License.
docker:
image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest
digest: sha256:e09366bdf0fd9c8976592988390b24d53583dd9f002d476934da43725adbb978
digest: sha256:7a40313731a7cb1454eef6b33d3446ebb121836738dc3ab3d2d3ded5268c35b6
49 changes: 24 additions & 25 deletions .kokoro/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -325,31 +325,30 @@ platformdirs==2.5.2 \
--hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \
--hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19
# via virtualenv
protobuf==3.20.1 \
--hash=sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf \
--hash=sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f \
--hash=sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f \
--hash=sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7 \
--hash=sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996 \
--hash=sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067 \
--hash=sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c \
--hash=sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7 \
--hash=sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9 \
--hash=sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c \
--hash=sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739 \
--hash=sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91 \
--hash=sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c \
--hash=sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153 \
--hash=sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9 \
--hash=sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388 \
--hash=sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e \
--hash=sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab \
--hash=sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde \
--hash=sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531 \
--hash=sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8 \
--hash=sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7 \
--hash=sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20 \
--hash=sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3
protobuf==3.20.2 \
--hash=sha256:03d76b7bd42ac4a6e109742a4edf81ffe26ffd87c5993126d894fe48a120396a \
--hash=sha256:09e25909c4297d71d97612f04f41cea8fa8510096864f2835ad2f3b3df5a5559 \
--hash=sha256:18e34a10ae10d458b027d7638a599c964b030c1739ebd035a1dfc0e22baa3bfe \
--hash=sha256:291fb4307094bf5ccc29f424b42268640e00d5240bf0d9b86bf3079f7576474d \
--hash=sha256:2c0b040d0b5d5d207936ca2d02f00f765906622c07d3fa19c23a16a8ca71873f \
--hash=sha256:384164994727f274cc34b8abd41a9e7e0562801361ee77437099ff6dfedd024b \
--hash=sha256:3cb608e5a0eb61b8e00fe641d9f0282cd0eedb603be372f91f163cbfbca0ded0 \
--hash=sha256:5d9402bf27d11e37801d1743eada54372f986a372ec9679673bfcc5c60441151 \
--hash=sha256:712dca319eee507a1e7df3591e639a2b112a2f4a62d40fe7832a16fd19151750 \
--hash=sha256:7a5037af4e76c975b88c3becdf53922b5ffa3f2cddf657574a4920a3b33b80f3 \
--hash=sha256:8228e56a865c27163d5d1d1771d94b98194aa6917bcfb6ce139cbfa8e3c27334 \
--hash=sha256:84a1544252a933ef07bb0b5ef13afe7c36232a774affa673fc3636f7cee1db6c \
--hash=sha256:84fe5953b18a383fd4495d375fe16e1e55e0a3afe7b4f7b4d01a3a0649fcda9d \
--hash=sha256:9c673c8bfdf52f903081816b9e0e612186684f4eb4c17eeb729133022d6032e3 \
--hash=sha256:9f876a69ca55aed879b43c295a328970306e8e80a263ec91cf6e9189243c613b \
--hash=sha256:a9e5ae5a8e8985c67e8944c23035a0dff2c26b0f5070b2f55b217a1c33bbe8b1 \
--hash=sha256:b4fdb29c5a7406e3f7ef176b2a7079baa68b5b854f364c21abe327bbeec01cdb \
--hash=sha256:c184485e0dfba4dfd451c3bd348c2e685d6523543a0f91b9fd4ae90eb09e8422 \
--hash=sha256:c9cdf251c582c16fd6a9f5e95836c90828d51b0069ad22f463761d27c6c19019 \
--hash=sha256:e39cf61bb8582bda88cdfebc0db163b774e7e03364bbf9ce1ead13863e81e359 \
--hash=sha256:e8fbc522303e09036c752a0afcc5c0603e917222d8bedc02813fd73b4b4ed804 \
--hash=sha256:f34464ab1207114e73bba0794d1257c150a2b89b7a9faf504e00af7c9fd58978 \
--hash=sha256:f52dabc96ca99ebd2169dadbe018824ebda08a795c7684a0b7d203a290f3adb0
# via
# gcp-docuploader
# gcp-releasetool
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ def _bucket_bound_hostname_url(host, scheme=None):
if url_parts.scheme and url_parts.netloc:
return host

return f"{scheme}://{host}/"
return f"{scheme}://{host}"


def _api_core_retry_to_resumable_media_retry(retry, num_retries=None):
Expand Down
9 changes: 5 additions & 4 deletions google/cloud/storage/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -2861,6 +2861,7 @@ def create_resumable_upload_session(
client=None,
timeout=_DEFAULT_TIMEOUT,
checksum=None,
predefined_acl=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
Expand Down Expand Up @@ -2942,6 +2943,9 @@ def create_resumable_upload_session(
delete the uploaded object automatically. Supported values
are "md5", "crc32c" and None. The default is None.

:type predefined_acl: str
:param predefined_acl: (Optional) Predefined access control list

:type if_generation_match: long
:param if_generation_match:
(Optional) See :ref:`using-if-generation-match`
Expand Down Expand Up @@ -3015,7 +3019,7 @@ def create_resumable_upload_session(
content_type,
size,
None,
predefined_acl=None,
predefined_acl=predefined_acl,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
Expand Down Expand Up @@ -3715,9 +3719,6 @@ def update_storage_class(
:param retry:
(Optional) How to retry the RPC. See: :ref:`configuring_retries`
"""
if new_class not in self.STORAGE_CLASSES:
raise ValueError(f"Invalid storage class: {new_class}")

# Update current blob's storage class prior to rewrite
self._patch_property("storageClass", new_class)

Expand Down
2 changes: 0 additions & 2 deletions google/cloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2654,8 +2654,6 @@ def storage_class(self, value):
or
:attr:`~google.cloud.storage.constants.DURABLE_REDUCED_AVAILABILITY_LEGACY_STORAGE_CLASS`,
"""
if value not in self.STORAGE_CLASSES:
raise ValueError(f"Invalid storage class: {value}")
self._patch_property("storageClass", value)

@property
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/storage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1759,7 +1759,7 @@ def generate_signed_post_policy_v4(
if virtual_hosted_style:
url = f"https://{bucket_name}.storage.googleapis.com/"
elif bucket_bound_hostname:
url = _bucket_bound_hostname_url(bucket_bound_hostname, scheme)
url = f"{_bucket_bound_hostname_url(bucket_bound_hostname, scheme)}/"
else:
url = f"https://storage.googleapis.com/{bucket_name}/"

Expand Down
8 changes: 0 additions & 8 deletions google/cloud/storage/hmac_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,6 @@ def state(self):

@state.setter
def state(self, value):
if value not in self._SETTABLE_STATES:
raise ValueError(
f"State may only be set to one of: {', '.join(self._SETTABLE_STATES)}"
)

self._properties["state"] = value

@property
Expand Down Expand Up @@ -289,9 +284,6 @@ def delete(self, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
:raises :class:`~google.api_core.exceptions.NotFound`:
if the key does not exist on the back-end.
"""
if self.state != self.INACTIVE_STATE:
raise ValueError("Cannot delete key if not in 'INACTIVE' state.")

qs_params = {}
if self.user_project is not None:
qs_params["userProject"] = self.user_project
Expand Down
6 changes: 3 additions & 3 deletions google/cloud/storage/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def exists(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
:raises ValueError: if the notification has no ID.
"""
if self.notification_id is None:
raise ValueError("Notification not intialized by server")
raise ValueError("Notification ID not set: set an explicit notification_id")

client = self._require_client(client)

Expand Down Expand Up @@ -352,7 +352,7 @@ def reload(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
:raises ValueError: if the notification has no ID.
"""
if self.notification_id is None:
raise ValueError("Notification not intialized by server")
raise ValueError("Notification ID not set: set an explicit notification_id")

client = self._require_client(client)

Expand Down Expand Up @@ -395,7 +395,7 @@ def delete(self, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY):
:raises ValueError: if the notification has no ID.
"""
if self.notification_id is None:
raise ValueError("Notification not intialized by server")
raise ValueError("Notification ID not set: set an explicit notification_id")

client = self._require_client(client)

Expand Down
2 changes: 1 addition & 1 deletion samples/snippets/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pytest==7.1.3
mock==4.0.3
backoff==2.1.2
backoff==2.2.1
4 changes: 2 additions & 2 deletions tests/unit/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -675,13 +675,13 @@ def _call_fut(self, **args):
return _bucket_bound_hostname_url(**args)

def test_full_hostname(self):
HOST = "scheme://domain.tcl/"
HOST = "scheme://domain.tcl"
self.assertEqual(self._call_fut(host=HOST), HOST)

def test_hostname_and_scheme(self):
HOST = "domain.tcl"
SCHEME = "scheme"
EXPECTED_URL = SCHEME + "://" + HOST + "/"
EXPECTED_URL = SCHEME + "://" + HOST

self.assertEqual(self._call_fut(host=HOST, scheme=SCHEME), EXPECTED_URL)

Expand Down
51 changes: 40 additions & 11 deletions tests/unit/test_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -3572,6 +3572,7 @@ def _create_resumable_upload_session_helper(
origin=None,
side_effect=None,
timeout=None,
predefined_acl=None,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
Expand Down Expand Up @@ -3611,6 +3612,7 @@ def _create_resumable_upload_session_helper(
size=size,
origin=origin,
client=client,
predefined_acl=predefined_acl,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
Expand All @@ -3629,6 +3631,9 @@ def _create_resumable_upload_session_helper(
)

qs_params = [("uploadType", "resumable")]
if predefined_acl is not None:
qs_params.append(("predefinedAcl", predefined_acl))

if if_generation_match is not None:
qs_params.append(("ifGenerationMatch", if_generation_match))

Expand Down Expand Up @@ -3672,6 +3677,9 @@ def test_create_resumable_upload_session_with_custom_timeout(self):
def test_create_resumable_upload_session_with_origin(self):
self._create_resumable_upload_session_helper(origin="http://google.com")

def test_create_resumable_upload_session_with_predefined_acl(self):
self._create_resumable_upload_session_helper(predefined_acl="private")

def test_create_resumable_upload_session_with_generation_match(self):
self._create_resumable_upload_session_helper(
if_generation_match=123456, if_metageneration_match=2
Expand Down Expand Up @@ -4994,17 +5002,6 @@ def test_rewrite_same_name_w_kms_key_w_version(self):
_target_object=dest,
)

def test_update_storage_class_invalid(self):
blob_name = "blob-name"
bucket = _Bucket()
blob = self._make_one(blob_name, bucket=bucket)
blob.rewrite = mock.Mock(spec=[])

with self.assertRaises(ValueError):
blob.update_storage_class("BOGUS")

blob.rewrite.assert_not_called()

def _update_storage_class_multi_pass_helper(self, **kw):
blob_name = "blob-name"
storage_class = "NEARLINE"
Expand Down Expand Up @@ -5215,6 +5212,38 @@ def test_update_storage_class_single_pass_w_retry(self):
retry = mock.Mock(spec=[])
self._update_storage_class_single_pass_helper(retry=retry)

def test_update_storage_class_invalid(self):
from google.cloud.exceptions import BadRequest

storage_class = "BOGUS"
blob_name = "blob-name"
client = mock.Mock(spec=[])
bucket = _Bucket(client=client)
blob = self._make_one(blob_name, bucket=bucket)
blob.rewrite = mock.Mock(spec=[])
blob.rewrite.side_effect = BadRequest("Invalid storage class")

with self.assertRaises(BadRequest):
blob.update_storage_class(storage_class)

# Test that invalid classes are allowed without client side validation.
# Fall back to server side validation and errors.
self.assertEqual(blob.storage_class, storage_class)

blob.rewrite.assert_called_once_with(
blob,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
if_source_generation_match=None,
if_source_generation_not_match=None,
if_source_metageneration_match=None,
if_source_metageneration_not_match=None,
timeout=self._get_default_timeout(),
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
)

def test_cache_control_getter(self):
BLOB_NAME = "blob-name"
bucket = _Bucket()
Expand Down
10 changes: 7 additions & 3 deletions tests/unit/test_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2813,11 +2813,15 @@ def test_storage_class_getter(self):
self.assertEqual(bucket.storage_class, NEARLINE_STORAGE_CLASS)

def test_storage_class_setter_invalid(self):
invalid_class = "BOGUS"
NAME = "name"
bucket = self._make_one(name=NAME)
with self.assertRaises(ValueError):
bucket.storage_class = "BOGUS"
self.assertFalse("storageClass" in bucket._changes)
bucket.storage_class = invalid_class

# Test that invalid classes are allowed without client side validation.
# Fall back to server side validation and errors.
self.assertEqual(bucket.storage_class, invalid_class)
self.assertTrue("storageClass" in bucket._changes)

def test_storage_class_setter_STANDARD(self):
from google.cloud.storage.constants import STANDARD_STORAGE_CLASS
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2572,7 +2572,7 @@ def test_get_signed_policy_v4_bucket_bound_hostname(self):
bucket_bound_hostname="https://bucket.bound_hostname",
credentials=_create_signing_credentials(),
)
self.assertEqual(policy["url"], "https://bucket.bound_hostname")
self.assertEqual(policy["url"], "https://bucket.bound_hostname/")

def test_get_signed_policy_v4_bucket_bound_hostname_with_scheme(self):
import datetime
Expand Down
9 changes: 5 additions & 4 deletions tests/unit/test_hmac_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,12 @@ def test_state_getter(self):
def test_state_setter_invalid_state(self):
metadata = self._make_one()
expected = "INVALID"
metadata.state = expected

with self.assertRaises(ValueError):
metadata.state = expected

self.assertIsNone(metadata.state)
# Test that invalid states are allowed without client side validation.
# Fall back to server side validation and errors.
self.assertEqual(metadata.state, expected)
self.assertEqual(metadata._properties["state"], expected)

def test_state_setter_inactive(self):
metadata = self._make_one()
Expand Down