def __copy_multipart(self, pool, src_bucket, src_key, dst_bucket, dst_key, part_size, **kwargs):
        """
        Copy a single S3 object to another S3 object, falling back to multipart copy where necessary

        NOTE: This is a private method and should only be called from within the `luigi.s3.copy` method

        :param pool: The threadpool to put the s3 copy processes onto
        :param src_bucket: source bucket name
        :param src_key: source key name
        :param dst_bucket: destination bucket name
        :param dst_key: destination key name
        :param key_size: size of the key to copy in bytes
        :param part_size: Part size in bytes. Must be >= 5MB and <= 5 GB.
        :param kwargs: Keyword arguments are passed to the boto function `initiate_multipart_upload`
        """

        source_bucket = self.s3.get_bucket(src_bucket, validate=True)
        dest_bucket = self.s3.get_bucket(dst_bucket, validate=True)

        key_size = source_bucket.lookup(src_key).size

        # We can't do a multipart copy on an empty Key, so handle this specially.
        # Also, don't bother using the multipart machinery if we're only dealing with a small non-multipart file
        if key_size == 0 or key_size <= part_size:
            result = pool.apply_async(dest_bucket.copy_key, args=(dst_key, src_bucket, src_key), kwds=kwargs)
            # Bubble up any errors we may encounter
            return result.get()

        mp = None

        try:
            mp = dest_bucket.initiate_multipart_upload(dst_key, **kwargs)
            cur_pos = 0

            # Store the results from the apply_async in a list so we can check for failures
            results = []

            # Calculate the number of chunks the file will be
            num_parts = (key_size + part_size - 1) // part_size

            for i in range(num_parts):
                # Issue an S3 copy request, one part at a time, from one S3 object to another
                part_start = cur_pos
                cur_pos += part_size
                part_end = min(cur_pos - 1, key_size - 1)
                part_num = i + 1
                results.append(pool.apply_async(mp.copy_part_from_key, args=(src_bucket, src_key, part_num, part_start, part_end)))
                logger.info('Requesting copy of %s/%s to %s/%s', part_num, num_parts, dst_bucket, dst_key)

            logger.info('Waiting for multipart copy of %s/%s to finish', dst_bucket, dst_key)

            # This will raise any exceptions in any of the copy threads
            for result in results:
                result.get()

            # finish the copy, making the file available in S3
            mp.complete_upload()
            return mp.key_name

        except:
            logger.info('Error during multipart s3 copy for %s/%s to %s/%s...', src_bucket, src_key, dst_bucket, dst_key)
            # cancel the copy so we don't get charged for storage consumed by copied parts
            if mp:
                mp.cancel_upload()
            raise
dataset/ETHPy150Open spotify/luigi/luigi/s3.py/S3Client.__copy_multipart/original