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.
- {google_cloud_storage-3.2.0/google_cloud_storage.egg-info → google_cloud_storage-3.3.1}/PKG-INFO +1 -1
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_upload.py +41 -3
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/upload.py +9 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/blob.py +49 -11
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/bucket.py +79 -7
- google_cloud_storage-3.3.1/google/cloud/storage/ip_filter.py +143 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/version.py +1 -1
- google_cloud_storage-3.3.1/google/cloud/storage_v2/__init__.py +140 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/gapic_metadata.json +263 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/gapic_version.py +16 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/py.typed +2 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/__init__.py +15 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/__init__.py +22 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/async_client.py +3443 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/client.py +3836 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/pagers.py +352 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/__init__.py +33 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/base.py +506 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/grpc.py +1185 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/services/storage/transports/grpc_asyncio.py +1349 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/types/__init__.py +132 -0
- google_cloud_storage-3.3.1/google/cloud/storage_v2/types/storage.py +4964 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1/google_cloud_storage.egg-info}/PKG-INFO +1 -1
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/SOURCES.txt +22 -1
- google_cloud_storage-3.3.1/tests/__init__.py +15 -0
- google_cloud_storage-3.3.1/tests/data/random_9_MiB_file +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test_upload.py +65 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__upload.py +28 -3
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/conftest.py +1 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test__signing.py +60 -10
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_blob.py +56 -24
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_bucket.py +68 -0
- google_cloud_storage-3.3.1/tests/unit/gapic/__init__.py +15 -0
- google_cloud_storage-3.3.1/tests/unit/gapic/storage_v2/__init__.py +15 -0
- google_cloud_storage-3.3.1/tests/unit/gapic/storage_v2/test_storage.py +11512 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_blob.py +25 -1
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_bucket.py +48 -0
- google_cloud_storage-3.3.1/tests/unit/test_ip_filter.py +106 -0
- google_cloud_storage-3.2.0/tests/system/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/LICENSE +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/MANIFEST.in +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/README.rst +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_http.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_download.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/common.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/py.typed +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/_request_helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/requests/download.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_opentelemetry_tracing.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_signing.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/acl.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/batch.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/client.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/constants.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/exceptions.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/fileio.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/hmac_key.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/iam.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/notification.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/retry.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/transfer_manager.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/dependency_links.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/not-zip-safe +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/requires.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google_cloud_storage.egg-info/top_level.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/setup.cfg +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/setup.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/README.md +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/conftest.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/retry_strategy_test_data.json +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/conformance/test_conformance.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/CloudPlatform_128px_Retina.png +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/five-point-one-mb-file.zip +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/data/simple.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/README.md +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/_perf_utils.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/benchmarking.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/perf/profile_w1r3.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/brotli.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/brotli.txt.br +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/favicon.ico +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/file.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/gzipped.txt +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/gzipped.txt.gz +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/image1.jpg +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/data/image2.jpg +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/credentials.json.enc +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/conftest.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/test_download.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/requests/test_upload.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/system/utils.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test__helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/requests/test_download.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__download.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/resumable_media/unit/test__helpers.py +0 -0
- {google_cloud_storage-3.2.0/tests → google_cloud_storage-3.3.1/tests/system}/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/_helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_client.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_fileio.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_hmac_key_metadata.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_kms_integration.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_notification.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/system/test_transfer_manager.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/__init__.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__helpers.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__http.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__opentelemetry_tracing.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test__signing.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_acl.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_batch.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_client.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_exceptions.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_fileio.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_hmac_key.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_notification.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_retry.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/test_transfer_manager.py +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/url_signer_v4_test_account.json +0 -0
- {google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/tests/unit/url_signer_v4_test_data.json +0 -0
{google_cloud_storage-3.2.0 → google_cloud_storage-3.3.1}/google/cloud/storage/_media/_upload.py
RENAMED
|
@@ -411,7 +411,12 @@ class ResumableUpload(UploadBase):
|
|
|
411
411
|
"""
|
|
412
412
|
|
|
413
413
|
def __init__(
|
|
414
|
-
self,
|
|
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,
|
|
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,
|
|
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
|
|
146
|
-
_MAX_MULTIPART_SIZE = 8388608 # 8
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 = {
|
|
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,
|
|
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": {
|
|
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
|
|
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 = {
|
|
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,
|
|
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
|