diff --git a/.changes/next-release/bugfix-awscrt-54174.json b/.changes/next-release/bugfix-awscrt-54174.json new file mode 100644 index 00000000..f8eca6a0 --- /dev/null +++ b/.changes/next-release/bugfix-awscrt-54174.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "``awscrt``", + "description": "Pass operation name to awscrt.s3 to improve error handling." +} diff --git a/s3transfer/crt.py b/s3transfer/crt.py index aa541843..5695451f 100644 --- a/s3transfer/crt.py +++ b/s3transfer/crt.py @@ -866,6 +866,14 @@ def _default_get_make_request_args( ), 'on_progress': self.get_crt_callback(future, 'progress'), } + + # For DEFAULT requests, CRT requires the official S3 operation name. + # So transform string like "delete_object" -> "DeleteObject". + if make_request_args['type'] == S3RequestType.DEFAULT: + make_request_args['operation_name'] = ''.join( + x.title() for x in request_type.split('_') + ) + if is_s3express_bucket(call_args.bucket): make_request_args['signing_config'] = AwsSigningConfig( algorithm=AwsSigningAlgorithm.V4_S3EXPRESS diff --git a/tests/functional/test_crt.py b/tests/functional/test_crt.py index 9a3afa75..23a2bee1 100644 --- a/tests/functional/test_crt.py +++ b/tests/functional/test_crt.py @@ -549,6 +549,7 @@ def test_delete(self): { 'request': mock.ANY, 'type': awscrt.s3.S3RequestType.DEFAULT, + 'operation_name': "DeleteObject", 'on_progress': mock.ANY, 'on_done': mock.ANY, }, diff --git a/tests/integration/test_crt.py b/tests/integration/test_crt.py index 7f16d85e..24fb06bc 100644 --- a/tests/integration/test_crt.py +++ b/tests/integration/test_crt.py @@ -13,6 +13,9 @@ import glob import io import os +from uuid import uuid4 + +from botocore.exceptions import ClientError from s3transfer.subscribers import BaseSubscriber from s3transfer.utils import OSUtils @@ -404,6 +407,17 @@ def test_delete(self): future.result() self.assertTrue(self.object_not_exists('foo.txt')) + def test_delete_exception_no_such_bucket(self): + # delete() uses awscrt.s3.S3RequestType.DEFAULT under the hood. + # Test that CRT exceptions translate properly into the botocore exceptions. + transfer = self._create_s3_transfer() + with self.assertRaises(ClientError) as ctx: + future = transfer.delete( + f"{self.bucket_name}-NONEXISTENT-{uuid4()}", "foo.txt" + ) + future.result() + self.assertEqual(ctx.exception.__class__.__name__, 'NoSuchBucket') + def test_many_files_download(self): transfer = self._create_s3_transfer()