google-cloud-storage 3.2.0__tar.gz → 3.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. {google_cloud_storage-3.2.0/google_cloud_storage.egg-info → google_cloud_storage-3.3.1}/PKG-INFO +1 -1
  2. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_upload.py +41 -3
  3. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/upload.py +9 -0
  4. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/blob.py +49 -11
  5. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/bucket.py +79 -7
  6. google_cloud_storage-3.3.1/google/cloud/storage/ip_filter.py +143 -0
  7. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/version.py +1 -1
  8. google_cloud_storage-3.3.1/google/cloud/storage_v2/__init__.py +140 -0
  9. google_cloud_storage-3.3.1/google/cloud/storage_v2/gapic_metadata.json +263 -0
  10. google_cloud_storage-3.3.1/google/cloud/storage_v2/gapic_version.py +16 -0
  11. google_cloud_storage-3.3.1/google/cloud/storage_v2/py.typed +2 -0
  12. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/__init__.py +15 -0
  13. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/__init__.py +22 -0
  14. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/async_client.py +3443 -0
  15. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/client.py +3836 -0
  16. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/pagers.py +352 -0
  17. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/__init__.py +33 -0
  18. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/base.py +506 -0
  19. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/grpc.py +1185 -0
  20. google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/grpc_asyncio.py +1349 -0
  21. google_cloud_storage-3.3.1/google/cloud/storage_v2/types/__init__.py +132 -0
  22. google_cloud_storage-3.3.1/google/cloud/storage_v2/types/storage.py +4964 -0
  23. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1/google_cloud_storage.egg-info}/PKG-INFO +1 -1
  24. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/SOURCES.txt +22 -1
  25. google_cloud_storage-3.3.1/tests/__init__.py +15 -0
  26. google_cloud_storage-3.3.1/tests/data/random_9_MiB_file +0 -0
  27. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test_upload.py +65 -0
  28. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__upload.py +28 -3
  29. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/conftest.py +1 -0
  30. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test__signing.py +60 -10
  31. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_blob.py +56 -24
  32. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_bucket.py +68 -0
  33. google_cloud_storage-3.3.1/tests/unit/gapic/__init__.py +15 -0
  34. google_cloud_storage-3.3.1/tests/unit/gapic/storage_v2/__init__.py +15 -0
  35. google_cloud_storage-3.3.1/tests/unit/gapic/storage_v2/test_storage.py +11512 -0
  36. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_blob.py +25 -1
  37. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_bucket.py +48 -0
  38. google_cloud_storage-3.3.1/tests/unit/test_ip_filter.py +106 -0
  39. google_cloud_storage-3.2.0/tests/system/__init__.py +0 -0
  40. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/LICENSE +0 -0
  41. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/MANIFEST.in +0 -0
  42. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/README.rst +0 -0
  43. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/__init__.py +0 -0
  44. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_helpers.py +0 -0
  45. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_http.py +0 -0
  46. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/__init__.py +0 -0
  47. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_download.py +0 -0
  48. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_helpers.py +0 -0
  49. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/common.py +0 -0
  50. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/py.typed +0 -0
  51. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/__init__.py +0 -0
  52. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/_request_helpers.py +0 -0
  53. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/download.py +0 -0
  54. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_opentelemetry_tracing.py +0 -0
  55. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_signing.py +0 -0
  56. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/acl.py +0 -0
  57. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/batch.py +0 -0
  58. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/client.py +0 -0
  59. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/constants.py +0 -0
  60. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/exceptions.py +0 -0
  61. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/fileio.py +0 -0
  62. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/hmac_key.py +0 -0
  63. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/iam.py +0 -0
  64. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/notification.py +0 -0
  65. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/retry.py +0 -0
  66. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/transfer_manager.py +0 -0
  67. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/dependency_links.txt +0 -0
  68. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/not-zip-safe +0 -0
  69. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/requires.txt +0 -0
  70. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/top_level.txt +0 -0
  71. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/setup.cfg +0 -0
  72. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/setup.py +0 -0
  73. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/README.md +0 -0
  74. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/__init__.py +0 -0
  75. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/conftest.py +0 -0
  76. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/retry_strategy_test_data.json +0 -0
  77. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/test_conformance.py +0 -0
  78. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/CloudPlatform_128px_Retina.png +0 -0
  79. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/five-point-one-mb-file.zip +0 -0
  80. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/simple.txt +0 -0
  81. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/README.md +0 -0
  82. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/_perf_utils.py +0 -0
  83. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/benchmarking.py +0 -0
  84. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/profile_w1r3.py +0 -0
  85. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/__init__.py +0 -0
  86. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/brotli.txt +0 -0
  87. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/brotli.txt.br +0 -0
  88. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/favicon.ico +0 -0
  89. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/file.txt +0 -0
  90. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/gzipped.txt +0 -0
  91. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/gzipped.txt.gz +0 -0
  92. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/image1.jpg +0 -0
  93. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/image2.jpg +0 -0
  94. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/__init__.py +0 -0
  95. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/credentials.json.enc +0 -0
  96. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/__init__.py +0 -0
  97. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/conftest.py +0 -0
  98. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/test_download.py +0 -0
  99. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/test_upload.py +0 -0
  100. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/utils.py +0 -0
  101. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/__init__.py +0 -0
  102. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/__init__.py +0 -0
  103. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test__helpers.py +0 -0
  104. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test_download.py +0 -0
  105. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__download.py +0 -0
  106. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__helpers.py +0 -0
  107. {google_cloud_storage-3.2.0/tests → google_cloud_storage-3.3.1/tests/system}/__init__.py +0 -0
  108. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/_helpers.py +0 -0
  109. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_client.py +0 -0
  110. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_fileio.py +0 -0
  111. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_hmac_key_metadata.py +0 -0
  112. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_kms_integration.py +0 -0
  113. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_notification.py +0 -0
  114. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_transfer_manager.py +0 -0
  115. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/__init__.py +0 -0
  116. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__helpers.py +0 -0
  117. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__http.py +0 -0
  118. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__opentelemetry_tracing.py +0 -0
  119. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__signing.py +0 -0
  120. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_acl.py +0 -0
  121. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_batch.py +0 -0
  122. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_client.py +0 -0
  123. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_exceptions.py +0 -0
  124. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_fileio.py +0 -0
  125. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_hmac_key.py +0 -0
  126. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_notification.py +0 -0
  127. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_retry.py +0 -0
  128. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_transfer_manager.py +0 -0
  129. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/url_signer_v4_test_account.json +0 -0
  130. {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/url_signer_v4_test_data.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-cloud-storage
3
- Version: 3.2.0
3
+ Version: 3.3.1
4
4
  Summary: Google Cloud Storage API client library
5
5
  Home-page: https://github.com/googleapis/python-storage
6
6
  Author: Google LLC
@@ -411,7 +411,12 @@ class ResumableUpload(UploadBase):
411
411
  """
412
412
 
413
413
  def __init__(
414
- self, upload_url, chunk_size, checksum="auto", headers=None, retry=DEFAULT_RETRY
414
+ self,
415
+ upload_url,
416
+ chunk_size,
417
+ checksum="auto",
418
+ headers=None,
419
+ retry=DEFAULT_RETRY,
415
420
  ):
416
421
  super(ResumableUpload, self).__init__(upload_url, headers=headers, retry=retry)
417
422
  if chunk_size % UPLOAD_CHUNK_SIZE != 0:
@@ -472,7 +477,12 @@ class ResumableUpload(UploadBase):
472
477
  return self._total_bytes
473
478
 
474
479
  def _prepare_initiate_request(
475
- self, stream, metadata, content_type, total_bytes=None, stream_final=True
480
+ self,
481
+ stream,
482
+ metadata,
483
+ content_type,
484
+ total_bytes=None,
485
+ stream_final=True,
476
486
  ):
477
487
  """Prepare the contents of HTTP request to initiate upload.
478
488
 
@@ -955,7 +965,12 @@ class XMLMPUContainer(UploadBase):
955
965
  """
956
966
 
957
967
  def __init__(
958
- self, upload_url, filename, headers=None, upload_id=None, retry=DEFAULT_RETRY
968
+ self,
969
+ upload_url,
970
+ filename,
971
+ headers=None,
972
+ upload_id=None,
973
+ retry=DEFAULT_RETRY,
959
974
  ):
960
975
  super().__init__(upload_url, headers=headers, retry=retry)
961
976
  self._filename = filename
@@ -1372,6 +1387,29 @@ class XMLMPUPart(UploadBase):
1372
1387
 
1373
1388
  .. _sans-I/O: https://sans-io.readthedocs.io/
1374
1389
  """
1390
+ # Data corruption errors shouldn't be considered as invalid responses,
1391
+ # So we handle them earlier than call to `_helpers.require_status_code`.
1392
+ # If the response is 400, we check for data corruption errors.
1393
+ if response.status_code == 400:
1394
+ root = ElementTree.fromstring(response.text)
1395
+ error_code = root.find("Code").text
1396
+ error_message = root.find("Message").text
1397
+ error_details = root.find("Details").text
1398
+ if error_code in ["InvalidDigest", "BadDigest", "CrcMismatch"]:
1399
+ raise DataCorruption(
1400
+ response,
1401
+ (
1402
+ "Checksum mismatch: checksum calculated by client and"
1403
+ " server did not match. Error code: {error_code},"
1404
+ " Error message: {error_message},"
1405
+ " Error details: {error_details}"
1406
+ ).format(
1407
+ error_code=error_code,
1408
+ error_message=error_message,
1409
+ error_details=error_details,
1410
+ ),
1411
+ )
1412
+
1375
1413
  _helpers.require_status_code(
1376
1414
  response,
1377
1415
  (http.client.OK,),
@@ -21,6 +21,7 @@ uploads that contain both metadata and a small file as payload.
21
21
 
22
22
  from google.cloud.storage._media import _upload
23
23
  from google.cloud.storage._media.requests import _request_helpers
24
+ from google.cloud.storage._media import _helpers
24
25
 
25
26
 
26
27
  class SimpleUpload(_request_helpers.RequestsMixin, _upload.SimpleUpload):
@@ -757,6 +758,14 @@ class XMLMPUPart(_request_helpers.RequestsMixin, _upload.XMLMPUPart):
757
758
  ~requests.Response: The HTTP response returned by ``transport``.
758
759
  """
759
760
  method, url, payload, headers = self._prepare_upload_request()
761
+ if self._checksum_object is not None:
762
+ checksum_digest_in_base64 = _helpers.prepare_checksum_digest(
763
+ self._checksum_object.digest()
764
+ )
765
+ if self._checksum_type == "crc32c":
766
+ headers["X-Goog-Hash"] = f"crc32c={checksum_digest_in_base64}"
767
+ elif self._checksum_type == "md5":
768
+ headers["X-Goog-Hash"] = f"md5={checksum_digest_in_base64}"
760
769
 
761
770
  # Wrap the request business logic in a function to be retried.
762
771
  def retriable_request():
@@ -14,8 +14,7 @@
14
14
 
15
15
  # pylint: disable=too-many-lines
16
16
 
17
- """Create / interact with Google Cloud Storage blobs.
18
- """
17
+ """Create / interact with Google Cloud Storage blobs."""
19
18
 
20
19
  import base64
21
20
  import copy
@@ -142,8 +141,8 @@ _GS_URL_REGEX_PATTERN = re.compile(
142
141
  r"(?P<scheme>gs)://(?P<bucket_name>[a-z0-9_.-]+)/(?P<object_name>.+)"
143
142
  )
144
143
 
145
- _DEFAULT_CHUNKSIZE = 104857600 # 1024 * 1024 B * 100 = 100 MB
146
- _MAX_MULTIPART_SIZE = 8388608 # 8 MB
144
+ _DEFAULT_CHUNKSIZE = 104857600 # 1024 * 1024 B * 100 = 100 MiB
145
+ _MAX_MULTIPART_SIZE = 8388608 # 8 MiB
147
146
 
148
147
  _logger = logging.getLogger(__name__)
149
148
 
@@ -181,6 +180,14 @@ class Blob(_PropertyMixin):
181
180
  :type generation: long
182
181
  :param generation:
183
182
  (Optional) If present, selects a specific revision of this object.
183
+
184
+ :type crc32c_checksum: str
185
+ :param crc32c_checksum:
186
+ (Optional) If set, the CRC32C checksum of the blob's content.
187
+ CRC32c checksum, as described in RFC 4960, Appendix B; encoded using
188
+ base64 in big-endian byte order. See
189
+ Apenndix B: https://datatracker.ietf.org/doc/html/rfc4960#appendix-B
190
+ base64: https://datatracker.ietf.org/doc/html/rfc4648#section-4
184
191
  """
185
192
 
186
193
  _chunk_size = None # Default value for each instance.
@@ -214,6 +221,7 @@ class Blob(_PropertyMixin):
214
221
  encryption_key=None,
215
222
  kms_key_name=None,
216
223
  generation=None,
224
+ crc32c_checksum=None,
217
225
  ):
218
226
  """
219
227
  property :attr:`name`
@@ -237,6 +245,9 @@ class Blob(_PropertyMixin):
237
245
  if generation is not None:
238
246
  self._properties["generation"] = generation
239
247
 
248
+ if crc32c_checksum is not None:
249
+ self._properties["crc32c"] = crc32c_checksum
250
+
240
251
  @property
241
252
  def bucket(self):
242
253
  """Bucket which contains the object.
@@ -1643,7 +1654,9 @@ class Blob(_PropertyMixin):
1643
1654
  :raises: :class:`google.cloud.exceptions.NotFound`
1644
1655
  """
1645
1656
  warnings.warn(
1646
- _DOWNLOAD_AS_STRING_DEPRECATED, PendingDeprecationWarning, stacklevel=2
1657
+ _DOWNLOAD_AS_STRING_DEPRECATED,
1658
+ PendingDeprecationWarning,
1659
+ stacklevel=2,
1647
1660
  )
1648
1661
  with create_trace_span(name="Storage.Blob.downloadAsString"):
1649
1662
  return self.download_as_bytes(
@@ -1999,12 +2012,18 @@ class Blob(_PropertyMixin):
1999
2012
  transport = self._get_transport(client)
2000
2013
  if "metadata" in self._properties and "metadata" not in self._changes:
2001
2014
  self._changes.add("metadata")
2015
+
2002
2016
  info = self._get_upload_arguments(client, content_type, command=command)
2003
2017
  headers, object_metadata, content_type = info
2004
2018
 
2019
+ if "crc32c" in self._properties:
2020
+ object_metadata["crc32c"] = self._properties["crc32c"]
2021
+
2005
2022
  hostname = _get_host_name(client._connection)
2006
2023
  base_url = _MULTIPART_URL_TEMPLATE.format(
2007
- hostname=hostname, bucket_path=self.bucket.path, api_version=_API_VERSION
2024
+ hostname=hostname,
2025
+ bucket_path=self.bucket.path,
2026
+ api_version=_API_VERSION,
2008
2027
  )
2009
2028
  name_value_pairs = []
2010
2029
 
@@ -2195,9 +2214,14 @@ class Blob(_PropertyMixin):
2195
2214
  if extra_headers is not None:
2196
2215
  headers.update(extra_headers)
2197
2216
 
2217
+ if "crc32c" in self._properties:
2218
+ object_metadata["crc32c"] = self._properties["crc32c"]
2219
+
2198
2220
  hostname = _get_host_name(client._connection)
2199
2221
  base_url = _RESUMABLE_URL_TEMPLATE.format(
2200
- hostname=hostname, bucket_path=self.bucket.path, api_version=_API_VERSION
2222
+ hostname=hostname,
2223
+ bucket_path=self.bucket.path,
2224
+ api_version=_API_VERSION,
2201
2225
  )
2202
2226
  name_value_pairs = []
2203
2227
 
@@ -2234,7 +2258,11 @@ class Blob(_PropertyMixin):
2234
2258
 
2235
2259
  upload_url = _add_query_parameters(base_url, name_value_pairs)
2236
2260
  upload = ResumableUpload(
2237
- upload_url, chunk_size, headers=headers, checksum=checksum, retry=retry
2261
+ upload_url,
2262
+ chunk_size,
2263
+ headers=headers,
2264
+ checksum=checksum,
2265
+ retry=retry,
2238
2266
  )
2239
2267
 
2240
2268
  upload.initiate(
@@ -3426,7 +3454,11 @@ class Blob(_PropertyMixin):
3426
3454
  return Policy.from_api_repr(info)
3427
3455
 
3428
3456
  def test_iam_permissions(
3429
- self, permissions, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY
3457
+ self,
3458
+ permissions,
3459
+ client=None,
3460
+ timeout=_DEFAULT_TIMEOUT,
3461
+ retry=DEFAULT_RETRY,
3430
3462
  ):
3431
3463
  """API call: test permissions
3432
3464
 
@@ -3693,7 +3725,10 @@ class Blob(_PropertyMixin):
3693
3725
 
3694
3726
  source_objects = []
3695
3727
  for source, source_generation in zip(sources, if_source_generation_match):
3696
- source_object = {"name": source.name, "generation": source.generation}
3728
+ source_object = {
3729
+ "name": source.name,
3730
+ "generation": source.generation,
3731
+ }
3697
3732
 
3698
3733
  preconditions = {}
3699
3734
  if source_generation is not None:
@@ -4154,7 +4189,10 @@ class Blob(_PropertyMixin):
4154
4189
  "encoding, errors and newline arguments are for text mode only"
4155
4190
  )
4156
4191
  return BlobWriter(
4157
- self, chunk_size=chunk_size, ignore_flush=ignore_flush, **kwargs
4192
+ self,
4193
+ chunk_size=chunk_size,
4194
+ ignore_flush=ignore_flush,
4195
+ **kwargs,
4158
4196
  )
4159
4197
  elif mode in ("r", "rt"):
4160
4198
  if ignore_flush:
@@ -56,6 +56,7 @@ from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_INHERITED
56
56
  from google.cloud.storage.constants import REGIONAL_LEGACY_STORAGE_CLASS
57
57
  from google.cloud.storage.constants import REGION_LOCATION_TYPE
58
58
  from google.cloud.storage.constants import STANDARD_STORAGE_CLASS
59
+ from google.cloud.storage.ip_filter import IPFilter
59
60
  from google.cloud.storage.notification import BucketNotification
60
61
  from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT
61
62
  from google.cloud.storage.retry import DEFAULT_RETRY
@@ -88,6 +89,7 @@ _LOCATION_SETTER_MESSAGE = (
88
89
  _FROM_STRING_MESSAGE = (
89
90
  "Bucket.from_string() is deprecated. " "Use Bucket.from_uri() instead."
90
91
  )
92
+ _IP_FILTER_PROPERTY = "ipFilter"
91
93
 
92
94
 
93
95
  def _blobs_page_start(iterator, page, response):
@@ -384,7 +386,10 @@ class LifecycleRuleSetStorageClass(dict):
384
386
  def __init__(self, storage_class, **kw):
385
387
  conditions = LifecycleRuleConditions(**kw)
386
388
  rule = {
387
- "action": {"type": "SetStorageClass", "storageClass": storage_class},
389
+ "action": {
390
+ "type": "SetStorageClass",
391
+ "storageClass": storage_class,
392
+ },
388
393
  "condition": dict(conditions),
389
394
  }
390
395
  super().__init__(rule)
@@ -844,6 +849,7 @@ class Bucket(_PropertyMixin):
844
849
  encryption_key=None,
845
850
  kms_key_name=None,
846
851
  generation=None,
852
+ crc32c_checksum=None,
847
853
  ):
848
854
  """Factory constructor for blob object.
849
855
 
@@ -871,6 +877,14 @@ class Bucket(_PropertyMixin):
871
877
  :param generation: (Optional) If present, selects a specific revision of
872
878
  this object.
873
879
 
880
+ :type crc32c_checksum: str
881
+ :param crc32c_checksum:
882
+ (Optional) If set, the CRC32C checksum of the blob's content.
883
+ CRC32c checksum, as described in RFC 4960, Appendix B; encoded using
884
+ base64 in big-endian byte order. See
885
+ Apenndix B: https://datatracker.ietf.org/doc/html/rfc4960#appendix-B
886
+ base64: https://datatracker.ietf.org/doc/html/rfc4648#section-4
887
+
874
888
  :rtype: :class:`google.cloud.storage.blob.Blob`
875
889
  :returns: The blob object created.
876
890
  """
@@ -881,6 +895,7 @@ class Bucket(_PropertyMixin):
881
895
  encryption_key=encryption_key,
882
896
  kms_key_name=kms_key_name,
883
897
  generation=generation,
898
+ crc32c_checksum=crc32c_checksum,
884
899
  )
885
900
 
886
901
  def notification(
@@ -2255,10 +2270,7 @@ class Bucket(_PropertyMixin):
2255
2270
  timeout=_DEFAULT_TIMEOUT,
2256
2271
  retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
2257
2272
  ):
2258
- """Move a blob to a new name within a single HNS bucket.
2259
-
2260
- *This feature is currently only supported for HNS (Heirarchical
2261
- Namespace) buckets.*
2273
+ """Move a blob to a new name atomically.
2262
2274
 
2263
2275
  If :attr:`user_project` is set on the bucket, bills the API request to that project.
2264
2276
 
@@ -3254,7 +3266,10 @@ class Bucket(_PropertyMixin):
3254
3266
  :type not_found_page: str
3255
3267
  :param not_found_page: The file to use when a page isn't found.
3256
3268
  """
3257
- data = {"mainPageSuffix": main_page_suffix, "notFoundPage": not_found_page}
3269
+ data = {
3270
+ "mainPageSuffix": main_page_suffix,
3271
+ "notFoundPage": not_found_page,
3272
+ }
3258
3273
  self._patch_property("website", data)
3259
3274
 
3260
3275
  def disable_website(self):
@@ -3386,7 +3401,11 @@ class Bucket(_PropertyMixin):
3386
3401
  return Policy.from_api_repr(info)
3387
3402
 
3388
3403
  def test_iam_permissions(
3389
- self, permissions, client=None, timeout=_DEFAULT_TIMEOUT, retry=DEFAULT_RETRY
3404
+ self,
3405
+ permissions,
3406
+ client=None,
3407
+ timeout=_DEFAULT_TIMEOUT,
3408
+ retry=DEFAULT_RETRY,
3390
3409
  ):
3391
3410
  """API call: test permissions
3392
3411
 
@@ -3890,6 +3909,59 @@ class Bucket(_PropertyMixin):
3890
3909
  query_parameters=query_parameters,
3891
3910
  )
3892
3911
 
3912
+ @property
3913
+ def ip_filter(self):
3914
+ """Retrieve or set the IP Filter configuration for this bucket.
3915
+
3916
+ See https://cloud.google.com/storage/docs/ip-filtering-overview and
3917
+ https://cloud.google.com/storage/docs/json_api/v1/buckets#ipFilter
3918
+
3919
+ .. note::
3920
+ The getter for this property returns an
3921
+ :class:`~google.cloud.storage.ip_filter.IPFilter` object, which is a
3922
+ structured representation of the bucket's IP filter configuration.
3923
+ Modifying the returned object has no effect. To update the bucket's
3924
+ IP filter, create and assign a new ``IPFilter`` object to this
3925
+ property and then call
3926
+ :meth:`~google.cloud.storage.bucket.Bucket.patch`.
3927
+
3928
+ .. code-block:: python
3929
+
3930
+ from google.cloud.storage.ip_filter import (
3931
+ IPFilter,
3932
+ PublicNetworkSource,
3933
+ )
3934
+
3935
+ ip_filter = IPFilter()
3936
+ ip_filter.mode = "Enabled"
3937
+ ip_filter.public_network_source = PublicNetworkSource(
3938
+ allowed_ip_cidr_ranges=["203.0.113.5/32"]
3939
+ )
3940
+ bucket.ip_filter = ip_filter
3941
+ bucket.patch()
3942
+
3943
+ :setter: Set the IP Filter configuration for this bucket.
3944
+ :getter: Gets the IP Filter configuration for this bucket.
3945
+
3946
+ :rtype: :class:`~google.cloud.storage.ip_filter.IPFilter` or ``NoneType``
3947
+ :returns:
3948
+ An ``IPFilter`` object representing the configuration, or ``None``
3949
+ if no filter is configured.
3950
+ """
3951
+ resource = self._properties.get(_IP_FILTER_PROPERTY)
3952
+ if resource:
3953
+ return IPFilter._from_api_resource(resource)
3954
+ return None
3955
+
3956
+ @ip_filter.setter
3957
+ def ip_filter(self, value):
3958
+ if value is None:
3959
+ self._patch_property(_IP_FILTER_PROPERTY, None)
3960
+ elif isinstance(value, IPFilter):
3961
+ self._patch_property(_IP_FILTER_PROPERTY, value._to_api_resource())
3962
+ else:
3963
+ self._patch_property(_IP_FILTER_PROPERTY, value)
3964
+
3893
3965
 
3894
3966
  class SoftDeletePolicy(dict):
3895
3967
  """Map a bucket's soft delete policy.
@@ -0,0 +1,143 @@
1
+ # Copyright 2014 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """IP Filter configuration for Google Cloud Storage Buckets."""
16
+
17
+ from typing import Dict, Any, Optional, List
18
+
19
+ _MODE = "mode"
20
+ _PUBLIC_NETWORK_SOURCE = "publicNetworkSource"
21
+ _VPC_NETWORK_SOURCES = "vpcNetworkSources"
22
+ _ALLOWED_IP_CIDR_RANGES = "allowedIpCidrRanges"
23
+ _NETWORK = "network"
24
+ _ALLOW_ALL_SERVICE_AGENT_ACCESS = "allowAllServiceAgentAccess"
25
+ _ALLOW_CROSS_ORG_VPCS = "allowCrossOrgVpcs"
26
+
27
+
28
+ class PublicNetworkSource:
29
+ """Represents a public network source for a GCS Bucket IP Filter.
30
+
31
+ :type allowed_ip_cidr_ranges: list(str) or None
32
+ :param allowed_ip_cidr_ranges: A list of public IPv4 or IPv6 ranges in
33
+ CIDR notation that are allowed to access
34
+ the bucket.
35
+ """
36
+
37
+ def __init__(self, allowed_ip_cidr_ranges: Optional[List[str]] = None):
38
+ self.allowed_ip_cidr_ranges = allowed_ip_cidr_ranges or []
39
+
40
+ def _to_api_resource(self) -> Dict[str, Any]:
41
+ """Serializes this object to a dictionary for API requests."""
42
+ return {_ALLOWED_IP_CIDR_RANGES: self.allowed_ip_cidr_ranges}
43
+
44
+
45
+ class VpcNetworkSource:
46
+ """Represents a VPC network source for a GCS Bucket IP Filter.
47
+
48
+ :type network: str
49
+ :param network: The resource name of the VPC network.
50
+
51
+ :type allowed_ip_cidr_ranges: list(str) or None
52
+ :param allowed_ip_cidr_ranges: A list of IPv4 or IPv6 ranges in CIDR
53
+ notation allowed to access the bucket
54
+ from this VPC.
55
+ """
56
+
57
+ def __init__(
58
+ self, network: str, allowed_ip_cidr_ranges: Optional[List[str]] = None
59
+ ):
60
+ self.network = network
61
+ self.allowed_ip_cidr_ranges = allowed_ip_cidr_ranges or []
62
+
63
+ def _to_api_resource(self) -> Dict[str, Any]:
64
+ """Serializes this object to a dictionary for API requests."""
65
+ return {
66
+ _NETWORK: self.network,
67
+ _ALLOWED_IP_CIDR_RANGES: self.allowed_ip_cidr_ranges,
68
+ }
69
+
70
+
71
+ class IPFilter:
72
+ """Represents a GCS Bucket IP Filter configuration.
73
+
74
+ This class is a helper for constructing the IP Filter dictionary to be
75
+ assigned to a bucket's ``ip_filter`` property.
76
+ """
77
+
78
+ """
79
+ Attributes:
80
+ mode (str): Required. The mode of the IP filter. Can be "Enabled" or "Disabled".
81
+ allow_all_service_agent_access (bool): Required. If True, allows Google
82
+ Cloud service agents to bypass the IP filter.
83
+ public_network_source (PublicNetworkSource): (Optional) The configuration
84
+ for requests from the public internet.
85
+ vpc_network_sources (list(VpcNetworkSource)): (Optional) A list of
86
+ configurations for requests from VPC networks.
87
+ allow_cross_org_vpcs (bool): (Optional) If True, allows VPCs from
88
+ other organizations to be used in the configuration.
89
+ """
90
+
91
+ def __init__(self):
92
+ self.mode: Optional[str] = None
93
+ self.public_network_source: Optional[PublicNetworkSource] = None
94
+ self.vpc_network_sources: List[VpcNetworkSource] = []
95
+ self.allow_all_service_agent_access: Optional[bool] = None
96
+ self.allow_cross_org_vpcs: Optional[bool] = None
97
+
98
+ @classmethod
99
+ def _from_api_resource(cls, resource: Dict[str, Any]) -> "IPFilter":
100
+ """Factory: creates an IPFilter instance from a server response."""
101
+ ip_filter = cls()
102
+ ip_filter.mode = resource.get(_MODE)
103
+ ip_filter.allow_all_service_agent_access = resource.get(
104
+ _ALLOW_ALL_SERVICE_AGENT_ACCESS, None
105
+ )
106
+
107
+ public_network_source_data = resource.get(_PUBLIC_NETWORK_SOURCE, None)
108
+ if public_network_source_data:
109
+ ip_filter.public_network_source = PublicNetworkSource(
110
+ allowed_ip_cidr_ranges=public_network_source_data.get(
111
+ _ALLOWED_IP_CIDR_RANGES, []
112
+ )
113
+ )
114
+
115
+ vns_res_list = resource.get(_VPC_NETWORK_SOURCES, [])
116
+ ip_filter.vpc_network_sources = [
117
+ VpcNetworkSource(
118
+ network=vns.get(_NETWORK),
119
+ allowed_ip_cidr_ranges=vns.get(_ALLOWED_IP_CIDR_RANGES, []),
120
+ )
121
+ for vns in vns_res_list
122
+ ]
123
+ ip_filter.allow_cross_org_vpcs = resource.get(_ALLOW_CROSS_ORG_VPCS, None)
124
+ return ip_filter
125
+
126
+ def _to_api_resource(self) -> Dict[str, Any]:
127
+ """Serializes this object to a dictionary for API requests."""
128
+ resource = {
129
+ _MODE: self.mode,
130
+ _ALLOW_ALL_SERVICE_AGENT_ACCESS: self.allow_all_service_agent_access,
131
+ }
132
+
133
+ if self.public_network_source:
134
+ resource[
135
+ _PUBLIC_NETWORK_SOURCE
136
+ ] = self.public_network_source._to_api_resource()
137
+ if self.vpc_network_sources is not None:
138
+ resource[_VPC_NETWORK_SOURCES] = [
139
+ vns._to_api_resource() for vns in self.vpc_network_sources
140
+ ]
141
+ if self.allow_cross_org_vpcs is not None:
142
+ resource[_ALLOW_CROSS_ORG_VPCS] = self.allow_cross_org_vpcs
143
+ return resource
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "3.2.0"
15
+ __version__ = "3.3.1"