diff --git a/s3fs/errors.py b/s3fs/errors.py index f5350670..25478772 100644 --- a/s3fs/errors.py +++ b/s3fs/errors.py @@ -140,15 +140,30 @@ def translate_boto_error(error, message=None, set_cause=True, *args, **kwargs): if error_response is None: # non-http error, or response is None: return error + + # AWS s3 as well as moto (CI Backend) respond with ResponseMetadata that contains RequestId as well as + # HTTPStatusCode. As of 6/17/2024, minio doesn't seem to add RequestId to the response. + request_id = '' + if 'ResponseMetadata' in error_response and 'RequestId' in error_response['ResponseMetadata']: + request_id = error_response['ResponseMetadata']['RequestId'] + http_code = '' + if 'ResponseMetadata' in error_response and 'HTTPStatusCode' in error_response['ResponseMetadata']: + http_code = error_response['ResponseMetadata']['HTTPStatusCode'] + code = error_response["Error"].get("Code") constructor = ERROR_CODE_TO_EXCEPTION.get(code) if constructor: if not message: message = error_response["Error"].get("Message", str(error)) + message = f'{message}, Request ID: {request_id}' if request_id else message + message = f'{message}, HTTP Status code: {http_code}' if http_code else message custom_exc = constructor(message, *args, **kwargs) else: # No match found, wrap this in an IOError with the appropriate message. - custom_exc = IOError(errno.EIO, message or str(error), *args) + message = message or str(error) + message = f'{message}, Request ID: {request_id}' if request_id else message + message = f'{message}, HTTP Status code: {http_code}' if http_code else message + custom_exc = IOError(errno.EIO, message, *args) if set_cause: custom_exc.__cause__ = error diff --git a/s3fs/tests/test_errors.py b/s3fs/tests/test_errors.py new file mode 100644 index 00000000..198e2b7b --- /dev/null +++ b/s3fs/tests/test_errors.py @@ -0,0 +1,24 @@ +import pytest +from s3fs.tests.test_s3fs import s3_base, s3, test_bucket_name +from s3fs import S3Map, S3FileSystem + +root = test_bucket_name + "/mapping" + + +def test_simple(s3): + d = s3.get_mapper(root) + assert not d + + assert list(d) == list(d.keys()) == [] + assert list(d.values()) == [] + assert list(d.items()) == [] + s3.get_mapper(root) + + try: + # Make an operation that raises IOError or OSError + f = d["nonexistent"] + print(f) + except OSError as e: + # verify error details + ... + diff --git a/s3fs/tests/test_s3fs.py b/s3fs/tests/test_s3fs.py index d3d90899..fc1221f6 100644 --- a/s3fs/tests/test_s3fs.py +++ b/s3fs/tests/test_s3fs.py @@ -98,7 +98,7 @@ def get_boto3_client(): # NB: we use the sync botocore client for setup session = Session() - return session.create_client("s3", endpoint_url=endpoint_uri) + return session.create_client("s3", endpoint_url=endpoint_uri, region_name="us-east-1") @pytest.fixture()