summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-07-11 23:59:47 +0300
committerrobot-piglet <[email protected]>2026-07-12 00:39:00 +0300
commit0bdaddf88145b3f3ea22ac1bae72df0f99a97428 (patch)
tree22c7fc1311fff3697edd88cf40f129c1b4f28531 /contrib/python
parent4f4f8ee8d3bdd09ef4448995e2c7bd29f6c37c31 (diff)
Intermediate changes
commit_hash:6a872f8a76031a194e46daac049491f222f4d74a
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/s3transfer/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/__init__.py12
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/constants.py8
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/crt.py29
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/manager.py32
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/upload.py40
-rw-r--r--contrib/python/s3transfer/py3/s3transfer/utils.py29
-rw-r--r--contrib/python/s3transfer/py3/tests/functional/test_crt.py41
-rw-r--r--contrib/python/s3transfer/py3/tests/functional/test_upload.py90
-rw-r--r--contrib/python/s3transfer/py3/tests/unit/test_utils.py28
-rw-r--r--contrib/python/s3transfer/py3/ya.make2
11 files changed, 257 insertions, 56 deletions
diff --git a/contrib/python/s3transfer/py3/.dist-info/METADATA b/contrib/python/s3transfer/py3/.dist-info/METADATA
index 9e232128de4..629c988c78a 100644
--- a/contrib/python/s3transfer/py3/.dist-info/METADATA
+++ b/contrib/python/s3transfer/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: s3transfer
-Version: 0.10.4
+Version: 0.11.0
Summary: An Amazon S3 Transfer Manager
Home-page: https://github.com/boto/s3transfer
Author: Amazon Web Services
diff --git a/contrib/python/s3transfer/py3/s3transfer/__init__.py b/contrib/python/s3transfer/py3/s3transfer/__init__.py
index 19ef3a6feff..de62a74eba5 100644
--- a/contrib/python/s3transfer/py3/s3transfer/__init__.py
+++ b/contrib/python/s3transfer/py3/s3transfer/__init__.py
@@ -145,7 +145,7 @@ import s3transfer.compat
from s3transfer.exceptions import RetriesExceededError, S3UploadFailedError
__author__ = 'Amazon Web Services'
-__version__ = '0.10.4'
+__version__ = '0.11.0'
class NullHandler(logging.Handler):
@@ -717,6 +717,9 @@ class S3Transfer:
def __init__(self, client, config=None, osutil=None):
self._client = client
+ self._client.meta.events.register(
+ 'before-call.s3.*', self._update_checksum_context
+ )
if config is None:
config = TransferConfig()
self._config = config
@@ -724,6 +727,13 @@ class S3Transfer:
osutil = OSUtils()
self._osutil = osutil
+ def _update_checksum_context(self, params, **kwargs):
+ request_context = params.get("context", {})
+ checksum_context = request_context.get("checksum", {})
+ if "request_algorithm" in checksum_context:
+ # Force request checksum algorithm in the header if specified.
+ checksum_context["request_algorithm"]["in"] = "header"
+
def upload_file(
self, filename, bucket, key, callback=None, extra_args=None
):
diff --git a/contrib/python/s3transfer/py3/s3transfer/constants.py b/contrib/python/s3transfer/py3/s3transfer/constants.py
index b07b1d47163..9a2ec9bb333 100644
--- a/contrib/python/s3transfer/py3/s3transfer/constants.py
+++ b/contrib/python/s3transfer/py3/s3transfer/constants.py
@@ -26,5 +26,13 @@ ALLOWED_DOWNLOAD_ARGS = [
'ExpectedBucketOwner',
]
+FULL_OBJECT_CHECKSUM_ARGS = [
+ 'ChecksumCRC32',
+ 'ChecksumCRC32C',
+ 'ChecksumCRC64NVME',
+ 'ChecksumSHA1',
+ 'ChecksumSHA256',
+]
+
USER_AGENT = f's3transfer/{s3transfer.__version__}'
PROCESS_USER_AGENT = f'{USER_AGENT} processpool'
diff --git a/contrib/python/s3transfer/py3/s3transfer/crt.py b/contrib/python/s3transfer/py3/s3transfer/crt.py
index df7eb26d910..37b041d8a96 100644
--- a/contrib/python/s3transfer/py3/s3transfer/crt.py
+++ b/contrib/python/s3transfer/py3/s3transfer/crt.py
@@ -39,7 +39,7 @@ from botocore.config import Config
from botocore.exceptions import NoCredentialsError
from botocore.utils import ArnParser, InvalidArnException
-from s3transfer.constants import MB
+from s3transfer.constants import FULL_OBJECT_CHECKSUM_ARGS, MB
from s3transfer.exceptions import TransferNotDoneError
from s3transfer.futures import BaseTransferFuture, BaseTransferMeta
from s3transfer.manager import TransferManager
@@ -491,6 +491,9 @@ class BotocoreCRTRequestSerializer(BaseCRTRequestSerializer):
self._client.meta.events.register(
'before-send.s3.*', self._make_fake_http_response
)
+ self._client.meta.events.register(
+ 'before-call.s3.*', self._remove_checksum_context
+ )
def _resolve_client_config(self, session, client_kwargs):
user_provided_config = None
@@ -620,6 +623,11 @@ class BotocoreCRTRequestSerializer(BaseCRTRequestSerializer):
error_class = self._client.exceptions.from_code(error_code)
return error_class(parsed_response, operation_name=operation_name)
+ def _remove_checksum_context(self, params, **kwargs):
+ request_context = params.get("context", {})
+ if "checksum" in request_context:
+ del request_context["checksum"]
+
class FakeRawResponse(BytesIO):
def stream(self, amt=1024, decode_content=None):
@@ -786,13 +794,18 @@ class S3ClientArgsCreator:
else:
call_args.extra_args["Body"] = call_args.fileobj
- checksum_algorithm = call_args.extra_args.pop(
- 'ChecksumAlgorithm', 'CRC32'
- ).upper()
- checksum_config = awscrt.s3.S3ChecksumConfig(
- algorithm=awscrt.s3.S3ChecksumAlgorithm[checksum_algorithm],
- location=awscrt.s3.S3ChecksumLocation.TRAILER,
- )
+ checksum_config = None
+ if not any(
+ checksum_arg in call_args.extra_args
+ for checksum_arg in FULL_OBJECT_CHECKSUM_ARGS
+ ):
+ checksum_algorithm = call_args.extra_args.pop(
+ 'ChecksumAlgorithm', 'CRC32'
+ ).upper()
+ checksum_config = awscrt.s3.S3ChecksumConfig(
+ algorithm=awscrt.s3.S3ChecksumAlgorithm[checksum_algorithm],
+ location=awscrt.s3.S3ChecksumLocation.TRAILER,
+ )
# Suppress botocore's automatic MD5 calculation by setting an override
# value that will get deleted in the BotocoreCRTRequestSerializer.
# As part of the CRT S3 request, we request the CRT S3 client to
diff --git a/contrib/python/s3transfer/py3/s3transfer/manager.py b/contrib/python/s3transfer/py3/s3transfer/manager.py
index 8db9a411a46..23b6f305ef0 100644
--- a/contrib/python/s3transfer/py3/s3transfer/manager.py
+++ b/contrib/python/s3transfer/py3/s3transfer/manager.py
@@ -16,7 +16,12 @@ import re
import threading
from s3transfer.bandwidth import BandwidthLimiter, LeakyBucket
-from s3transfer.constants import ALLOWED_DOWNLOAD_ARGS, KB, MB
+from s3transfer.constants import (
+ ALLOWED_DOWNLOAD_ARGS,
+ FULL_OBJECT_CHECKSUM_ARGS,
+ KB,
+ MB,
+)
from s3transfer.copies import CopySubmissionTask
from s3transfer.delete import DeleteSubmissionTask
from s3transfer.download import DownloadSubmissionTask
@@ -35,8 +40,8 @@ from s3transfer.utils import (
OSUtils,
SlidingWindowSemaphore,
TaskSemaphore,
- add_s3express_defaults,
get_callbacks,
+ set_default_checksum_algorithm,
signal_not_transferring,
signal_transferring,
)
@@ -157,7 +162,7 @@ class TransferConfig:
class TransferManager:
ALLOWED_DOWNLOAD_ARGS = ALLOWED_DOWNLOAD_ARGS
- ALLOWED_UPLOAD_ARGS = [
+ _ALLOWED_SHARED_ARGS = [
'ACL',
'CacheControl',
'ChecksumAlgorithm',
@@ -187,7 +192,16 @@ class TransferManager:
'WebsiteRedirectLocation',
]
- ALLOWED_COPY_ARGS = ALLOWED_UPLOAD_ARGS + [
+ ALLOWED_UPLOAD_ARGS = (
+ _ALLOWED_SHARED_ARGS
+ + [
+ 'ChecksumType',
+ 'MpuObjectSize',
+ ]
+ + FULL_OBJECT_CHECKSUM_ARGS
+ )
+
+ ALLOWED_COPY_ARGS = _ALLOWED_SHARED_ARGS + [
'CopySourceIfMatch',
'CopySourceIfModifiedSince',
'CopySourceIfNoneMatch',
@@ -315,13 +329,13 @@ class TransferManager:
:rtype: s3transfer.futures.TransferFuture
:returns: Transfer future representing the upload
"""
- if extra_args is None:
- extra_args = {}
+
+ extra_args = extra_args.copy() if extra_args else {}
if subscribers is None:
subscribers = []
self._validate_all_known_args(extra_args, self.ALLOWED_UPLOAD_ARGS)
self._validate_if_bucket_supported(bucket)
- self._add_operation_defaults(bucket, extra_args)
+ self._add_operation_defaults(extra_args)
call_args = CallArgs(
fileobj=fileobj,
bucket=bucket,
@@ -504,8 +518,8 @@ class TransferManager:
"must be one of: {}".format(kwarg, ', '.join(allowed))
)
- def _add_operation_defaults(self, bucket, extra_args):
- add_s3express_defaults(bucket, extra_args)
+ def _add_operation_defaults(self, extra_args):
+ set_default_checksum_algorithm(extra_args)
def _submit_transfer(
self, call_args, submission_task_cls, extra_main_kwargs=None
diff --git a/contrib/python/s3transfer/py3/s3transfer/upload.py b/contrib/python/s3transfer/py3/s3transfer/upload.py
index 0347857b5d2..a3db8f899e2 100644
--- a/contrib/python/s3transfer/py3/s3transfer/upload.py
+++ b/contrib/python/s3transfer/py3/s3transfer/upload.py
@@ -14,6 +14,7 @@ import math
from io import BytesIO
from s3transfer.compat import readable, seekable
+from s3transfer.constants import FULL_OBJECT_CHECKSUM_ARGS
from s3transfer.futures import IN_MEMORY_UPLOAD_TAG
from s3transfer.tasks import (
CompleteMultipartUploadTask,
@@ -512,6 +513,10 @@ class UploadNonSeekableInputManager(UploadInputManager):
class UploadSubmissionTask(SubmissionTask):
"""Task for submitting tasks to execute an upload"""
+ PUT_OBJECT_BLOCKLIST = ["ChecksumType", "MpuObjectSize"]
+
+ CREATE_MULTIPART_BLOCKLIST = FULL_OBJECT_CHECKSUM_ARGS + ["MpuObjectSize"]
+
UPLOAD_PART_ARGS = [
'ChecksumAlgorithm',
'SSECustomerKey',
@@ -527,7 +532,9 @@ class UploadSubmissionTask(SubmissionTask):
'SSECustomerKeyMD5',
'RequestPayer',
'ExpectedBucketOwner',
- ]
+ 'ChecksumType',
+ 'MpuObjectSize',
+ ] + FULL_OBJECT_CHECKSUM_ARGS
def _get_upload_input_manager_cls(self, transfer_future):
"""Retrieves a class for managing input for an upload based on file type
@@ -621,6 +628,10 @@ class UploadSubmissionTask(SubmissionTask):
):
call_args = transfer_future.meta.call_args
+ put_object_extra_args = self._extra_put_object_args(
+ call_args.extra_args
+ )
+
# Get any tags that need to be associated to the put object task
put_object_tag = self._get_upload_task_tag(
upload_input_manager, 'put_object'
@@ -638,7 +649,7 @@ class UploadSubmissionTask(SubmissionTask):
),
'bucket': call_args.bucket,
'key': call_args.key,
- 'extra_args': call_args.extra_args,
+ 'extra_args': put_object_extra_args,
},
is_final=True,
),
@@ -656,6 +667,19 @@ class UploadSubmissionTask(SubmissionTask):
):
call_args = transfer_future.meta.call_args
+ # When a user provided checksum is passed, set "ChecksumType" to "FULL_OBJECT"
+ # and "ChecksumAlgorithm" to the related algorithm.
+ for checksum in FULL_OBJECT_CHECKSUM_ARGS:
+ if checksum in call_args.extra_args:
+ call_args.extra_args["ChecksumType"] = "FULL_OBJECT"
+ call_args.extra_args["ChecksumAlgorithm"] = checksum.replace(
+ "Checksum", ""
+ )
+
+ create_multipart_extra_args = self._extra_create_multipart_args(
+ call_args.extra_args
+ )
+
# Submit the request to create a multipart upload.
create_multipart_future = self._transfer_coordinator.submit(
request_executor,
@@ -665,7 +689,7 @@ class UploadSubmissionTask(SubmissionTask):
'client': client,
'bucket': call_args.bucket,
'key': call_args.key,
- 'extra_args': call_args.extra_args,
+ 'extra_args': create_multipart_extra_args,
},
),
)
@@ -739,6 +763,16 @@ class UploadSubmissionTask(SubmissionTask):
def _extra_complete_multipart_args(self, extra_args):
return get_filtered_dict(extra_args, self.COMPLETE_MULTIPART_ARGS)
+ def _extra_create_multipart_args(self, extra_args):
+ return get_filtered_dict(
+ extra_args, blocklisted_keys=self.CREATE_MULTIPART_BLOCKLIST
+ )
+
+ def _extra_put_object_args(self, extra_args):
+ return get_filtered_dict(
+ extra_args, blocklisted_keys=self.PUT_OBJECT_BLOCKLIST
+ )
+
def _get_upload_task_tag(self, upload_input_manager, operation_name):
tag = None
if upload_input_manager.stores_body_in_memory(operation_name):
diff --git a/contrib/python/s3transfer/py3/s3transfer/utils.py b/contrib/python/s3transfer/py3/s3transfer/utils.py
index 98742236217..f2c4e06c54e 100644
--- a/contrib/python/s3transfer/py3/s3transfer/utils.py
+++ b/contrib/python/s3transfer/py3/s3transfer/utils.py
@@ -26,10 +26,11 @@ from botocore.exceptions import (
ReadTimeoutError,
ResponseStreamingError,
)
-from botocore.httpchecksum import AwsChunkedWrapper
+from botocore.httpchecksum import DEFAULT_CHECKSUM_ALGORITHM, AwsChunkedWrapper
from botocore.utils import is_s3express_bucket
from s3transfer.compat import SOCKET_ERROR, fallocate, rename_file
+from s3transfer.constants import FULL_OBJECT_CHECKSUM_ARGS
MAX_PARTS = 10000
# The maximum file size you can upload via S3 per request.
@@ -148,20 +149,27 @@ def invoke_progress_callbacks(callbacks, bytes_transferred):
callback(bytes_transferred=bytes_transferred)
-def get_filtered_dict(original_dict, whitelisted_keys):
- """Gets a dictionary filtered by whitelisted keys
+def get_filtered_dict(
+ original_dict, whitelisted_keys=None, blocklisted_keys=None
+):
+ """Gets a dictionary filtered by whitelisted and blocklisted keys.
:param original_dict: The original dictionary of arguments to source keys
and values.
:param whitelisted_key: A list of keys to include in the filtered
dictionary.
+ :param blocklisted_key: A list of keys to exclude in the filtered
+ dictionary.
:returns: A dictionary containing key/values from the original dictionary
- whose key was included in the whitelist
+ whose key was included in the whitelist and/or not included in the
+ blocklist.
"""
filtered_dict = {}
for key, value in original_dict.items():
- if key in whitelisted_keys:
+ if (whitelisted_keys and key in whitelisted_keys) or (
+ blocklisted_keys and key not in blocklisted_keys
+ ):
filtered_dict[key] = value
return filtered_dict
@@ -809,6 +817,17 @@ class ChunksizeAdjuster:
def add_s3express_defaults(bucket, extra_args):
+ """
+ This function has been deprecated, but is kept for backwards compatibility.
+ This function is subject to removal in a future release.
+ """
if is_s3express_bucket(bucket) and "ChecksumAlgorithm" not in extra_args:
# Default Transfer Operations to S3Express to use CRC32
extra_args["ChecksumAlgorithm"] = "crc32"
+
+
+def set_default_checksum_algorithm(extra_args):
+ """Set the default algorithm to CRC32 if not specified by the user."""
+ if any(checksum in extra_args for checksum in FULL_OBJECT_CHECKSUM_ARGS):
+ return
+ extra_args.setdefault("ChecksumAlgorithm", DEFAULT_CHECKSUM_ALGORITHM)
diff --git a/contrib/python/s3transfer/py3/tests/functional/test_crt.py b/contrib/python/s3transfer/py3/tests/functional/test_crt.py
index 815e92e12f3..5ef25144e85 100644
--- a/contrib/python/s3transfer/py3/tests/functional/test_crt.py
+++ b/contrib/python/s3transfer/py3/tests/functional/test_crt.py
@@ -116,6 +116,7 @@ class TestCRTTransferManager(unittest.TestCase):
expected_body_content=None,
expected_content_length=None,
expected_missing_headers=None,
+ expected_extra_headers=None,
):
if expected_host is None:
expected_host = self.expected_host
@@ -138,12 +139,15 @@ class TestCRTTransferManager(unittest.TestCase):
crt_http_request.headers.get('Content-Length'),
str(expected_content_length),
)
+ header_names = [
+ header[0].lower() for header in crt_http_request.headers
+ ]
if expected_missing_headers is not None:
- header_names = [
- header[0].lower() for header in crt_http_request.headers
- ]
for expected_missing_header in expected_missing_headers:
self.assertNotIn(expected_missing_header.lower(), header_names)
+ if expected_extra_headers is not None:
+ for header, value in expected_extra_headers.items():
+ self.assertEqual(crt_http_request.headers.get(header), value)
def _assert_expected_s3express_request(
self, make_request_kwargs, expected_http_method='GET'
@@ -447,6 +451,37 @@ class TestCRTTransferManager(unittest.TestCase):
expected_http_method='PUT',
)
+ def test_upload_with_full_checksum(self):
+ future = self.transfer_manager.upload(
+ self.filename,
+ self.bucket,
+ self.key,
+ {"ChecksumCRC32": "abc123"},
+ [self.record_subscriber],
+ )
+ future.result()
+
+ callargs_kwargs = self.s3_crt_client.make_request.call_args[1]
+ self.assertEqual(
+ callargs_kwargs,
+ {
+ 'request': mock.ANY,
+ 'type': awscrt.s3.S3RequestType.PUT_OBJECT,
+ 'send_filepath': self.filename,
+ 'on_progress': mock.ANY,
+ 'on_done': mock.ANY,
+ 'checksum_config': None,
+ },
+ )
+ self._assert_expected_crt_http_request(
+ callargs_kwargs["request"],
+ expected_http_method='PUT',
+ expected_content_length=len(self.expected_content),
+ expected_missing_headers=['Content-MD5'],
+ expected_extra_headers={"x-amz-checksum-crc32": "abc123"},
+ )
+ self._assert_subscribers_called(future)
+
def test_download(self):
future = self.transfer_manager.download(
self.bucket, self.key, self.filename, {}, [self.record_subscriber]
diff --git a/contrib/python/s3transfer/py3/tests/functional/test_upload.py b/contrib/python/s3transfer/py3/tests/functional/test_upload.py
index a7afd4c2ca3..00ac284f62f 100644
--- a/contrib/python/s3transfer/py3/tests/functional/test_upload.py
+++ b/contrib/python/s3transfer/py3/tests/functional/test_upload.py
@@ -22,6 +22,7 @@ from botocore.exceptions import ClientError
from botocore.stub import ANY
from s3transfer.manager import TransferConfig, TransferManager
+from s3transfer.upload import UploadSubmissionTask
from s3transfer.utils import ChunksizeAdjuster
from __tests__ import (
BaseGeneralInterfaceTest,
@@ -147,7 +148,12 @@ class TestNonMultipartUpload(BaseUploadTest):
if bucket is None:
bucket = self.bucket
- expected_params = {'Body': ANY, 'Bucket': bucket, 'Key': self.key}
+ expected_params = {
+ 'Body': ANY,
+ 'Bucket': bucket,
+ 'Key': self.key,
+ 'ChecksumAlgorithm': 'CRC32',
+ }
if extra_expected_params:
expected_params.update(extra_expected_params)
upload_response = self.create_stubbed_responses()[0]
@@ -186,7 +192,6 @@ class TestNonMultipartUpload(BaseUploadTest):
self.assertFalse("ChecksumAlgorithm" in self.extra_args)
self.add_put_object_response_with_default_expected_params(
- extra_expected_params={'ChecksumAlgorithm': 'crc32'},
bucket=s3express_bucket,
)
future = self.manager.upload(
@@ -278,7 +283,12 @@ class TestNonMultipartUpload(BaseUploadTest):
def test_allowed_upload_params_are_valid(self):
op_model = self.client.meta.service_model.operation_model('PutObject')
- for allowed_upload_arg in self._manager.ALLOWED_UPLOAD_ARGS:
+ allowed_upload_arg = [
+ arg
+ for arg in self._manager.ALLOWED_UPLOAD_ARGS
+ if arg not in UploadSubmissionTask.PUT_OBJECT_BLOCKLIST
+ ]
+ for allowed_upload_arg in allowed_upload_arg:
self.assertIn(allowed_upload_arg, op_model.input_shape.members)
def test_upload_with_bandwidth_limiter(self):
@@ -367,7 +377,11 @@ class TestMultipartUpload(BaseUploadTest):
if bucket is None:
bucket = self.bucket
- expected_params = {'Bucket': bucket, 'Key': self.key}
+ expected_params = {
+ 'Bucket': bucket,
+ 'Key': self.key,
+ 'ChecksumAlgorithm': 'CRC32',
+ }
if extra_expected_params:
expected_params.update(extra_expected_params)
response = self.create_stubbed_responses()[0]
@@ -392,15 +406,15 @@ class TestMultipartUpload(BaseUploadTest):
'UploadId': self.multipart_id,
'Body': ANY,
'PartNumber': i + 1,
+ 'ChecksumAlgorithm': 'CRC32',
}
if extra_expected_params:
expected_params.update(extra_expected_params)
- # If ChecksumAlgorithm is present stub the response checksums
- if 'ChecksumAlgorithm' in extra_expected_params:
- name = extra_expected_params['ChecksumAlgorithm']
- checksum_member = f'Checksum{name.upper()}'
- response = upload_part_response['service_response']
- response[checksum_member] = 'sum%s==' % (i + 1)
+
+ name = expected_params['ChecksumAlgorithm']
+ checksum_member = f'Checksum{name.upper()}'
+ response = upload_part_response['service_response']
+ response[checksum_member] = f'sum{i+1}=='
upload_part_response['expected_params'] = expected_params
self.stubber.add_response(**upload_part_response)
@@ -419,9 +433,21 @@ class TestMultipartUpload(BaseUploadTest):
'UploadId': self.multipart_id,
'MultipartUpload': {
'Parts': [
- {'ETag': 'etag-1', 'PartNumber': 1},
- {'ETag': 'etag-2', 'PartNumber': 2},
- {'ETag': 'etag-3', 'PartNumber': 3},
+ {
+ 'ETag': 'etag-1',
+ 'PartNumber': 1,
+ 'ChecksumCRC32': 'sum1==',
+ },
+ {
+ 'ETag': 'etag-2',
+ 'PartNumber': 2,
+ 'ChecksumCRC32': 'sum2==',
+ },
+ {
+ 'ETag': 'etag-3',
+ 'PartNumber': 3,
+ 'ChecksumCRC32': 'sum3==',
+ },
]
},
}
@@ -539,17 +565,22 @@ class TestMultipartUpload(BaseUploadTest):
self.stubber.add_response(
method='create_multipart_upload',
service_response={'UploadId': self.multipart_id},
- expected_params={'Bucket': self.bucket, 'Key': self.key},
+ expected_params={
+ 'Bucket': self.bucket,
+ 'Key': self.key,
+ 'ChecksumAlgorithm': 'CRC32',
+ },
)
self.stubber.add_response(
method='upload_part',
- service_response={'ETag': 'etag-1'},
+ service_response={'ETag': 'etag-1', 'ChecksumCRC32': 'sum1=='},
expected_params={
'Bucket': self.bucket,
'Body': ANY,
'Key': self.key,
'UploadId': self.multipart_id,
'PartNumber': 1,
+ 'ChecksumAlgorithm': 'CRC32',
},
)
# With the upload part failing this should immediately initiate
@@ -639,13 +670,13 @@ class TestMultipartUpload(BaseUploadTest):
# ChecksumAlgorithm should be passed on the create_multipart call
self.add_create_multipart_response_with_default_expected_params(
- extra_expected_params={'ChecksumAlgorithm': 'crc32'},
+ extra_expected_params={'ChecksumAlgorithm': 'CRC32'},
bucket=s3express_bucket,
)
# ChecksumAlgorithm should be forwarded and a SHA1 will come back
self.add_upload_part_responses_with_default_expected_params(
- extra_expected_params={'ChecksumAlgorithm': 'crc32'},
+ extra_expected_params={'ChecksumAlgorithm': 'CRC32'},
bucket=s3express_bucket,
)
@@ -705,3 +736,28 @@ class TestMultipartUpload(BaseUploadTest):
)
future.result()
self.assert_expected_client_calls_were_correct()
+
+ def test_multipart_upload_with_full_object_checksum_args(self):
+ checksum_type_param = {
+ 'ChecksumType': 'FULL_OBJECT',
+ }
+ params = {
+ 'ChecksumCRC32': 'example-checksum-value',
+ 'MpuObjectSize': 12345,
+ }
+ params.update(checksum_type_param)
+ self.extra_args.update(params)
+
+ self.add_create_multipart_response_with_default_expected_params(
+ extra_expected_params=checksum_type_param
+ )
+
+ self.add_upload_part_responses_with_default_expected_params()
+ self.add_complete_multipart_response_with_default_expected_params(
+ extra_expected_params=params
+ )
+ future = self.manager.upload(
+ self.filename, self.bucket, self.key, self.extra_args
+ )
+ future.result()
+ self.assert_expected_client_calls_were_correct()
diff --git a/contrib/python/s3transfer/py3/tests/unit/test_utils.py b/contrib/python/s3transfer/py3/tests/unit/test_utils.py
index 444bad0baa7..81f54e1a1e0 100644
--- a/contrib/python/s3transfer/py3/tests/unit/test_utils.py
+++ b/contrib/python/s3transfer/py3/tests/unit/test_utils.py
@@ -38,13 +38,13 @@ from s3transfer.utils import (
SlidingWindowSemaphore,
StreamReaderProgress,
TaskSemaphore,
- add_s3express_defaults,
calculate_num_parts,
calculate_range_parameter,
get_callbacks,
get_filtered_dict,
invoke_progress_callbacks,
random_file_extension,
+ set_default_checksum_algorithm,
)
from __tests__ import NonSeekableWriter, RecordingSubscriber, mock, unittest
@@ -1192,32 +1192,44 @@ class TestAdjustChunksize(unittest.TestCase):
self.assertEqual(new_size, MAX_SINGLE_UPLOAD_SIZE)
-class TestS3ExpressDefaults:
+class TestS3Defaults:
@pytest.mark.parametrize(
"bucket,extra_args,expected",
(
(
"mytestbucket--usw2-az2--x-s3",
{},
- {"ChecksumAlgorithm": "crc32"},
+ {"ChecksumAlgorithm": "CRC32"},
),
(
"mytestbucket--usw2-az2--x-s3",
{"Some": "Setting"},
- {"ChecksumAlgorithm": "crc32", "Some": "Setting"},
+ {"ChecksumAlgorithm": "CRC32", "Some": "Setting"},
+ ),
+ (
+ "mytestbucket--usw2-az2--x-s3",
+ {"ChecksumAlgorithm": "sha256"},
+ {"ChecksumAlgorithm": "sha256"},
),
(
"mytestbucket",
{},
- {},
+ {"ChecksumAlgorithm": "CRC32"},
),
(
- "mytestbucket--usw2-az2--x-s3",
+ "mytestbucket",
+ {"Some": "Setting"},
+ {"ChecksumAlgorithm": "CRC32", "Some": "Setting"},
+ ),
+ (
+ "mytestbucket",
{"ChecksumAlgorithm": "sha256"},
{"ChecksumAlgorithm": "sha256"},
),
),
)
- def test_add_s3express_defaults(self, bucket, extra_args, expected):
- add_s3express_defaults(bucket, extra_args)
+ def test_set_default_checksum_algorithm(
+ self, bucket, extra_args, expected
+ ):
+ set_default_checksum_algorithm(extra_args)
assert extra_args == expected
diff --git a/contrib/python/s3transfer/py3/ya.make b/contrib/python/s3transfer/py3/ya.make
index c5dfe51ccb8..57776c6f7ce 100644
--- a/contrib/python/s3transfer/py3/ya.make
+++ b/contrib/python/s3transfer/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(0.10.4)
+VERSION(0.11.0)
LICENSE(Apache-2.0)