def open(self, filename, mode): if 'wb' in mode: fileobj = six.BytesIO() self.filemap[filename] = fileobj return closing(fileobj) else: return closing(self.filemap[filename])
def test_multipart_download_with_multiple_parts(self): client = mock.Mock() response_body = b'foobarbaz' client.get_object.return_value = {'Body': six.BytesIO(response_body)} # For testing purposes, we're testing with a multipart threshold # of 4 bytes and a chunksize of 4 bytes. Given b'foobarbaz', # this should result in 3 calls. In python slices this would be: # r[0:4], r[4:8], r[8:9]. But the Range param will be slightly # different because they use inclusive ranges. config = TransferConfig(multipart_threshold=4, multipart_chunksize=4) downloader = MultipartDownloader(client, config, InMemoryOSLayer({}), SequentialExecutor) downloader.download_file('bucket', 'key', 'filename', len(response_body), {}) # We're storing these in **extra because the assertEqual # below is really about verifying we have the correct value # for the Range param. extra = {'Bucket': 'bucket', 'Key': 'key'} self.assertEqual(client.get_object.call_args_list, # Note these are inclusive ranges. [mock.call(Range='bytes=0-3', **extra), mock.call(Range='bytes=4-7', **extra), mock.call(Range='bytes=8-', **extra)])
def test_can_upload_archive(self): body = six.BytesIO(b"bytes content") response = self.client.upload_archive(vaultName=self.VAULT_NAME, archiveDescription='test upload', body=body) self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 201) archive_id = response['archiveId'] response = self.client.delete_archive(vaultName=self.VAULT_NAME, archiveId=archive_id) self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 204)
def test_download_below_multipart_threshold(self): below_threshold = 20 osutil = InMemoryOSLayer({'smallfile': b'hello world'}) transfer = S3Transfer(self.client, osutil=osutil) self.client.head_object.return_value = { 'ContentLength': below_threshold} self.client.get_object.return_value = { 'Body': six.BytesIO(b'foobar') } transfer.download_file('bucket', 'key', 'smallfile') self.client.get_object.assert_called_with(Bucket='bucket', Key='key')
def test_get_object_stream_is_retried_and_succeeds(self): below_threshold = 20 osutil = InMemoryOSLayer({'smallfile': b'hello world'}) transfer = S3Transfer(self.client, osutil=osutil) self.client.head_object.return_value = { 'ContentLength': below_threshold} self.client.get_object.side_effect = [ # First request fails. socket.error("fake error"), # Second succeeds. {'Body': six.BytesIO(b'foobar')} ] transfer.download_file('bucket', 'key', '/tmp/smallfile') self.assertEqual(self.client.get_object.call_count, 2)
def test_multipart_download_uses_correct_client_calls(self): client = mock.Mock() response_body = b'foobarbaz' client.get_object.return_value = {'Body': six.BytesIO(response_body)} downloader = MultipartDownloader(client, TransferConfig(), InMemoryOSLayer({}), SequentialExecutor) downloader.download_file('bucket', 'key', 'filename', len(response_body), {}) client.get_object.assert_called_with( Range='bytes=0-', Bucket='bucket', Key='key' )
def test_io_thread_failure_triggers_shutdown(self): client = mock.Mock() response_body = b'foobarbaz' client.get_object.return_value = {'Body': six.BytesIO(response_body)} os_layer = mock.Mock() mock_fileobj = mock.MagicMock() mock_fileobj.__enter__.return_value = mock_fileobj mock_fileobj.write.side_effect = Exception("fake IO error") os_layer.open.return_value = mock_fileobj downloader = MultipartDownloader(client, TransferConfig(), os_layer, SequentialExecutor) # We're verifying that the exception raised from the IO future # propogates back up via download_file(). with self.assertRaisesRegexp(Exception, "fake IO error"): downloader.download_file('bucket', 'key', 'filename', len(response_body), {})
def test_download_file_fowards_extra_args(self): extra_args = { 'SSECustomerKey': 'foo', 'SSECustomerAlgorithm': 'AES256', } below_threshold = 20 osutil = InMemoryOSLayer({'smallfile': b'hello world'}) transfer = S3Transfer(self.client, osutil=osutil) self.client.head_object.return_value = { 'ContentLength': below_threshold} self.client.get_object.return_value = { 'Body': six.BytesIO(b'foobar') } transfer.download_file('bucket', 'key', '/tmp/smallfile', extra_args=extra_args) # Note that we need to invoke the HeadObject call # and the PutObject call with the extra_args. # This is necessary. Trying to HeadObject an SSE object # will return a 400 if you don't provide the required # params. self.client.get_object.assert_called_with( Bucket='bucket', Key='key', SSECustomerAlgorithm='AES256', SSECustomerKey='foo')
def test_download_futures_fail_triggers_shutdown(self): class FailedDownloadParts(SequentialExecutor): def __init__(self, max_workers): self.is_first = True def submit(self, function): future = super(FailedDownloadParts, self).submit(function) if self.is_first: # This is the download_parts_thread. future.set_exception( Exception("fake download parts error")) self.is_first = False return future client = mock.Mock() response_body = b'foobarbaz' client.get_object.return_value = {'Body': six.BytesIO(response_body)} downloader = MultipartDownloader(client, TransferConfig(), InMemoryOSLayer({}), FailedDownloadParts) with self.assertRaisesRegexp(Exception, "fake download parts error"): downloader.download_file('bucket', 'key', 'filename', len(response_body), {})
def open_file_chunk_reader(self, filename, start_byte, size, callback): return closing(six.BytesIO(self.filemap[filename]))