aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/s3transfer/copies.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/s3transfer/copies.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/s3transfer/copies.py')
-rw-r--r--.venv/lib/python3.12/site-packages/s3transfer/copies.py388
1 files changed, 388 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/s3transfer/copies.py b/.venv/lib/python3.12/site-packages/s3transfer/copies.py
new file mode 100644
index 00000000..c2ae9ce0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/s3transfer/copies.py
@@ -0,0 +1,388 @@
+# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import copy
+import math
+
+from s3transfer.tasks import (
+ CompleteMultipartUploadTask,
+ CreateMultipartUploadTask,
+ SubmissionTask,
+ Task,
+)
+from s3transfer.utils import (
+ ChunksizeAdjuster,
+ calculate_range_parameter,
+ get_callbacks,
+ get_filtered_dict,
+)
+
+
+class CopySubmissionTask(SubmissionTask):
+ """Task for submitting tasks to execute a copy"""
+
+ EXTRA_ARGS_TO_HEAD_ARGS_MAPPING = {
+ 'CopySourceIfMatch': 'IfMatch',
+ 'CopySourceIfModifiedSince': 'IfModifiedSince',
+ 'CopySourceIfNoneMatch': 'IfNoneMatch',
+ 'CopySourceIfUnmodifiedSince': 'IfUnmodifiedSince',
+ 'CopySourceSSECustomerKey': 'SSECustomerKey',
+ 'CopySourceSSECustomerAlgorithm': 'SSECustomerAlgorithm',
+ 'CopySourceSSECustomerKeyMD5': 'SSECustomerKeyMD5',
+ 'RequestPayer': 'RequestPayer',
+ 'ExpectedBucketOwner': 'ExpectedBucketOwner',
+ }
+
+ UPLOAD_PART_COPY_ARGS = [
+ 'CopySourceIfMatch',
+ 'CopySourceIfModifiedSince',
+ 'CopySourceIfNoneMatch',
+ 'CopySourceIfUnmodifiedSince',
+ 'CopySourceSSECustomerKey',
+ 'CopySourceSSECustomerAlgorithm',
+ 'CopySourceSSECustomerKeyMD5',
+ 'SSECustomerKey',
+ 'SSECustomerAlgorithm',
+ 'SSECustomerKeyMD5',
+ 'RequestPayer',
+ 'ExpectedBucketOwner',
+ ]
+
+ CREATE_MULTIPART_ARGS_BLACKLIST = [
+ 'CopySourceIfMatch',
+ 'CopySourceIfModifiedSince',
+ 'CopySourceIfNoneMatch',
+ 'CopySourceIfUnmodifiedSince',
+ 'CopySourceSSECustomerKey',
+ 'CopySourceSSECustomerAlgorithm',
+ 'CopySourceSSECustomerKeyMD5',
+ 'MetadataDirective',
+ 'TaggingDirective',
+ ]
+
+ COMPLETE_MULTIPART_ARGS = [
+ 'SSECustomerKey',
+ 'SSECustomerAlgorithm',
+ 'SSECustomerKeyMD5',
+ 'RequestPayer',
+ 'ExpectedBucketOwner',
+ ]
+
+ def _submit(
+ self, client, config, osutil, request_executor, transfer_future
+ ):
+ """
+ :param client: The client associated with the transfer manager
+
+ :type config: s3transfer.manager.TransferConfig
+ :param config: The transfer config associated with the transfer
+ manager
+
+ :type osutil: s3transfer.utils.OSUtil
+ :param osutil: The os utility associated to the transfer manager
+
+ :type request_executor: s3transfer.futures.BoundedExecutor
+ :param request_executor: The request executor associated with the
+ transfer manager
+
+ :type transfer_future: s3transfer.futures.TransferFuture
+ :param transfer_future: The transfer future associated with the
+ transfer request that tasks are being submitted for
+ """
+ # Determine the size if it was not provided
+ if transfer_future.meta.size is None:
+ # If a size was not provided figure out the size for the
+ # user. Note that we will only use the client provided to
+ # the TransferManager. If the object is outside of the region
+ # of the client, they may have to provide the file size themselves
+ # with a completely new client.
+ call_args = transfer_future.meta.call_args
+ head_object_request = (
+ self._get_head_object_request_from_copy_source(
+ call_args.copy_source
+ )
+ )
+ extra_args = call_args.extra_args
+
+ # Map any values that may be used in the head object that is
+ # used in the copy object
+ for param, value in extra_args.items():
+ if param in self.EXTRA_ARGS_TO_HEAD_ARGS_MAPPING:
+ head_object_request[
+ self.EXTRA_ARGS_TO_HEAD_ARGS_MAPPING[param]
+ ] = value
+
+ response = call_args.source_client.head_object(
+ **head_object_request
+ )
+ transfer_future.meta.provide_transfer_size(
+ response['ContentLength']
+ )
+
+ # If it is greater than threshold do a multipart copy, otherwise
+ # do a regular copy object.
+ if transfer_future.meta.size < config.multipart_threshold:
+ self._submit_copy_request(
+ client, config, osutil, request_executor, transfer_future
+ )
+ else:
+ self._submit_multipart_request(
+ client, config, osutil, request_executor, transfer_future
+ )
+
+ def _submit_copy_request(
+ self, client, config, osutil, request_executor, transfer_future
+ ):
+ call_args = transfer_future.meta.call_args
+
+ # Get the needed progress callbacks for the task
+ progress_callbacks = get_callbacks(transfer_future, 'progress')
+
+ # Submit the request of a single copy.
+ self._transfer_coordinator.submit(
+ request_executor,
+ CopyObjectTask(
+ transfer_coordinator=self._transfer_coordinator,
+ main_kwargs={
+ 'client': client,
+ 'copy_source': call_args.copy_source,
+ 'bucket': call_args.bucket,
+ 'key': call_args.key,
+ 'extra_args': call_args.extra_args,
+ 'callbacks': progress_callbacks,
+ 'size': transfer_future.meta.size,
+ },
+ is_final=True,
+ ),
+ )
+
+ def _submit_multipart_request(
+ self, client, config, osutil, request_executor, transfer_future
+ ):
+ call_args = transfer_future.meta.call_args
+
+ # Submit the request to create a multipart upload and make sure it
+ # does not include any of the arguments used for copy part.
+ create_multipart_extra_args = {}
+ for param, val in call_args.extra_args.items():
+ if param not in self.CREATE_MULTIPART_ARGS_BLACKLIST:
+ create_multipart_extra_args[param] = val
+
+ create_multipart_future = self._transfer_coordinator.submit(
+ request_executor,
+ CreateMultipartUploadTask(
+ transfer_coordinator=self._transfer_coordinator,
+ main_kwargs={
+ 'client': client,
+ 'bucket': call_args.bucket,
+ 'key': call_args.key,
+ 'extra_args': create_multipart_extra_args,
+ },
+ ),
+ )
+
+ # Determine how many parts are needed based on filesize and
+ # desired chunksize.
+ part_size = config.multipart_chunksize
+ adjuster = ChunksizeAdjuster()
+ part_size = adjuster.adjust_chunksize(
+ part_size, transfer_future.meta.size
+ )
+ num_parts = int(
+ math.ceil(transfer_future.meta.size / float(part_size))
+ )
+
+ # Submit requests to upload the parts of the file.
+ part_futures = []
+ progress_callbacks = get_callbacks(transfer_future, 'progress')
+
+ for part_number in range(1, num_parts + 1):
+ extra_part_args = self._extra_upload_part_args(
+ call_args.extra_args
+ )
+ # The part number for upload part starts at 1 while the
+ # range parameter starts at zero, so just subtract 1 off of
+ # the part number
+ extra_part_args['CopySourceRange'] = calculate_range_parameter(
+ part_size,
+ part_number - 1,
+ num_parts,
+ transfer_future.meta.size,
+ )
+ # Get the size of the part copy as well for the progress
+ # callbacks.
+ size = self._get_transfer_size(
+ part_size,
+ part_number - 1,
+ num_parts,
+ transfer_future.meta.size,
+ )
+ # Get the checksum algorithm of the multipart request.
+ checksum_algorithm = call_args.extra_args.get("ChecksumAlgorithm")
+ part_futures.append(
+ self._transfer_coordinator.submit(
+ request_executor,
+ CopyPartTask(
+ transfer_coordinator=self._transfer_coordinator,
+ main_kwargs={
+ 'client': client,
+ 'copy_source': call_args.copy_source,
+ 'bucket': call_args.bucket,
+ 'key': call_args.key,
+ 'part_number': part_number,
+ 'extra_args': extra_part_args,
+ 'callbacks': progress_callbacks,
+ 'size': size,
+ 'checksum_algorithm': checksum_algorithm,
+ },
+ pending_main_kwargs={
+ 'upload_id': create_multipart_future
+ },
+ ),
+ )
+ )
+
+ complete_multipart_extra_args = self._extra_complete_multipart_args(
+ call_args.extra_args
+ )
+ # Submit the request to complete the multipart upload.
+ self._transfer_coordinator.submit(
+ request_executor,
+ CompleteMultipartUploadTask(
+ transfer_coordinator=self._transfer_coordinator,
+ main_kwargs={
+ 'client': client,
+ 'bucket': call_args.bucket,
+ 'key': call_args.key,
+ 'extra_args': complete_multipart_extra_args,
+ },
+ pending_main_kwargs={
+ 'upload_id': create_multipart_future,
+ 'parts': part_futures,
+ },
+ is_final=True,
+ ),
+ )
+
+ def _get_head_object_request_from_copy_source(self, copy_source):
+ if isinstance(copy_source, dict):
+ return copy.copy(copy_source)
+ else:
+ raise TypeError(
+ 'Expecting dictionary formatted: '
+ '{"Bucket": bucket_name, "Key": key} '
+ f'but got {copy_source} or type {type(copy_source)}.'
+ )
+
+ def _extra_upload_part_args(self, extra_args):
+ # Only the args in COPY_PART_ARGS actually need to be passed
+ # onto the upload_part_copy calls.
+ return get_filtered_dict(extra_args, self.UPLOAD_PART_COPY_ARGS)
+
+ def _extra_complete_multipart_args(self, extra_args):
+ return get_filtered_dict(extra_args, self.COMPLETE_MULTIPART_ARGS)
+
+ def _get_transfer_size(
+ self, part_size, part_index, num_parts, total_transfer_size
+ ):
+ if part_index == num_parts - 1:
+ # The last part may be different in size then the rest of the
+ # parts.
+ return total_transfer_size - (part_index * part_size)
+ return part_size
+
+
+class CopyObjectTask(Task):
+ """Task to do a nonmultipart copy"""
+
+ def _main(
+ self, client, copy_source, bucket, key, extra_args, callbacks, size
+ ):
+ """
+ :param client: The client to use when calling PutObject
+ :param copy_source: The CopySource parameter to use
+ :param bucket: The name of the bucket to copy to
+ :param key: The name of the key to copy to
+ :param extra_args: A dictionary of any extra arguments that may be
+ used in the upload.
+ :param callbacks: List of callbacks to call after copy
+ :param size: The size of the transfer. This value is passed into
+ the callbacks
+
+ """
+ client.copy_object(
+ CopySource=copy_source, Bucket=bucket, Key=key, **extra_args
+ )
+ for callback in callbacks:
+ callback(bytes_transferred=size)
+
+
+class CopyPartTask(Task):
+ """Task to upload a part in a multipart copy"""
+
+ def _main(
+ self,
+ client,
+ copy_source,
+ bucket,
+ key,
+ upload_id,
+ part_number,
+ extra_args,
+ callbacks,
+ size,
+ checksum_algorithm=None,
+ ):
+ """
+ :param client: The client to use when calling PutObject
+ :param copy_source: The CopySource parameter to use
+ :param bucket: The name of the bucket to upload to
+ :param key: The name of the key to upload to
+ :param upload_id: The id of the upload
+ :param part_number: The number representing the part of the multipart
+ upload
+ :param extra_args: A dictionary of any extra arguments that may be
+ used in the upload.
+ :param callbacks: List of callbacks to call after copy part
+ :param size: The size of the transfer. This value is passed into
+ the callbacks
+ :param checksum_algorithm: The algorithm that was used to create the multipart
+ upload
+
+ :rtype: dict
+ :returns: A dictionary representing a part::
+
+ {'Etag': etag_value, 'PartNumber': part_number}
+
+ This value can be appended to a list to be used to complete
+ the multipart upload. If a checksum is in the response,
+ it will also be included.
+ """
+ response = client.upload_part_copy(
+ CopySource=copy_source,
+ Bucket=bucket,
+ Key=key,
+ UploadId=upload_id,
+ PartNumber=part_number,
+ **extra_args,
+ )
+ for callback in callbacks:
+ callback(bytes_transferred=size)
+ etag = response['CopyPartResult']['ETag']
+ part_metadata = {'ETag': etag, 'PartNumber': part_number}
+ if checksum_algorithm:
+ checksum_member = f'Checksum{checksum_algorithm.upper()}'
+ if checksum_member in response['CopyPartResult']:
+ part_metadata[checksum_member] = response['CopyPartResult'][
+ checksum_member
+ ]
+ return part_metadata