1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
"""S3 error codes adapted into more natural Python ones.
Adapted from: https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
"""
import errno
import functools
# Fallback values since some systems might not have these.
ENAMETOOLONG = getattr(errno, "ENAMETOOLONG", errno.EINVAL)
ENOTEMPTY = getattr(errno, "ENOTEMPTY", errno.EINVAL)
EMSGSIZE = getattr(errno, "EMSGSIZE", errno.EINVAL)
EREMOTEIO = getattr(errno, "EREMOTEIO", errno.EIO)
EREMCHG = getattr(errno, "EREMCHG", errno.ENOENT)
ERROR_CODE_TO_EXCEPTION = {
"AccessDenied": PermissionError,
"AccountProblem": PermissionError,
"AllAccessDisabled": PermissionError,
"AmbiguousGrantByEmailAddress": functools.partial(IOError, errno.EINVAL),
"AuthorizationHeaderMalformed": functools.partial(IOError, errno.EINVAL),
"BadDigest": functools.partial(IOError, errno.EINVAL),
"BucketAlreadyExists": FileExistsError,
"BucketAlreadyOwnedByYou": FileExistsError,
"BucketNotEmpty": functools.partial(IOError, ENOTEMPTY),
"CredentialsNotSupported": functools.partial(IOError, errno.EINVAL),
"CrossLocationLoggingProhibited": PermissionError,
"EntityTooSmall": functools.partial(IOError, errno.EINVAL),
"EntityTooLarge": functools.partial(IOError, EMSGSIZE),
"ExpiredToken": PermissionError,
"IllegalLocationConstraintException": PermissionError,
"IllegalVersioningConfigurationException": functools.partial(IOError, errno.EINVAL),
"IncompleteBody": functools.partial(IOError, errno.EINVAL),
"IncorrectNumberOfFilesInPostRequest": functools.partial(IOError, errno.EINVAL),
"InlineDataTooLarge": functools.partial(IOError, EMSGSIZE),
"InternalError": functools.partial(IOError, EREMOTEIO),
"InvalidAccessKeyId": PermissionError,
"InvalidAddressingHeader": functools.partial(IOError, errno.EINVAL),
"InvalidArgument": functools.partial(IOError, errno.EINVAL),
"InvalidBucketName": functools.partial(IOError, errno.EINVAL),
"InvalidBucketState": functools.partial(IOError, errno.EPERM),
"InvalidDigest": functools.partial(IOError, errno.EINVAL),
"InvalidEncryptionAlgorithmError": functools.partial(IOError, errno.EINVAL),
"InvalidLocationConstraint": functools.partial(IOError, errno.EINVAL),
"InvalidObjectState": PermissionError,
"InvalidPart": functools.partial(IOError, errno.EINVAL),
"InvalidPartOrder": functools.partial(IOError, errno.EINVAL),
"InvalidPayer": PermissionError,
"InvalidPolicyDocument": functools.partial(IOError, errno.EINVAL),
"InvalidRange": functools.partial(IOError, errno.EINVAL),
"InvalidRequest": functools.partial(IOError, errno.EINVAL),
"InvalidSecurity": PermissionError,
"InvalidSOAPRequest": functools.partial(IOError, errno.EINVAL),
"InvalidStorageClass": functools.partial(IOError, errno.EINVAL),
"InvalidTargetBucketForLogging": functools.partial(IOError, errno.EINVAL),
"InvalidToken": functools.partial(IOError, errno.EINVAL),
"InvalidURI": functools.partial(IOError, errno.EINVAL),
"KeyTooLongError": functools.partial(IOError, ENAMETOOLONG),
"MalformedACLError": functools.partial(IOError, errno.EINVAL),
"MalformedPOSTRequest": functools.partial(IOError, errno.EINVAL),
"MalformedXML": functools.partial(IOError, errno.EINVAL),
"MaxMessageLengthExceeded": functools.partial(IOError, EMSGSIZE),
"MaxPostPreDataLengthExceededError": functools.partial(IOError, EMSGSIZE),
"MetadataTooLarge": functools.partial(IOError, EMSGSIZE),
"MethodNotAllowed": functools.partial(IOError, errno.EPERM),
"MissingAttachment": functools.partial(IOError, errno.EINVAL),
"MissingContentLength": functools.partial(IOError, errno.EINVAL),
"MissingRequestBodyError": functools.partial(IOError, errno.EINVAL),
"MissingSecurityElement": functools.partial(IOError, errno.EINVAL),
"MissingSecurityHeader": functools.partial(IOError, errno.EINVAL),
"NoLoggingStatusForKey": functools.partial(IOError, errno.EINVAL),
"NoSuchBucket": FileNotFoundError,
"NoSuchBucketPolicy": FileNotFoundError,
"NoSuchKey": FileNotFoundError,
"NoSuchLifecycleConfiguration": FileNotFoundError,
"NoSuchUpload": FileNotFoundError,
"NoSuchVersion": FileNotFoundError,
"NotImplemented": functools.partial(IOError, errno.ENOSYS),
"NotSignedUp": PermissionError,
"OperationAborted": functools.partial(IOError, errno.EBUSY),
"PermanentRedirect": functools.partial(IOError, EREMCHG),
"PreconditionFailed": functools.partial(IOError, errno.EINVAL),
"Redirect": functools.partial(IOError, EREMCHG),
"RestoreAlreadyInProgress": functools.partial(IOError, errno.EBUSY),
"RequestIsNotMultiPartContent": functools.partial(IOError, errno.EINVAL),
"RequestTimeout": TimeoutError,
"RequestTimeTooSkewed": PermissionError,
"RequestTorrentOfBucketError": functools.partial(IOError, errno.EPERM),
"SignatureDoesNotMatch": PermissionError,
"ServiceUnavailable": functools.partial(IOError, errno.EBUSY),
"SlowDown": functools.partial(IOError, errno.EBUSY),
"TemporaryRedirect": functools.partial(IOError, EREMCHG),
"TokenRefreshRequired": functools.partial(IOError, errno.EINVAL),
"TooManyBuckets": functools.partial(IOError, errno.EINVAL),
"UnexpectedContent": functools.partial(IOError, errno.EINVAL),
"UnresolvableGrantByEmailAddress": functools.partial(IOError, errno.EINVAL),
"UserKeyMustBeSpecified": functools.partial(IOError, errno.EINVAL),
"301": functools.partial(IOError, EREMCHG), # PermanentRedirect
"307": functools.partial(IOError, EREMCHG), # Redirect
"400": functools.partial(IOError, errno.EINVAL),
"403": PermissionError,
"404": FileNotFoundError,
"405": functools.partial(IOError, errno.EPERM),
"409": functools.partial(IOError, errno.EBUSY),
"412": functools.partial(IOError, errno.EINVAL), # PreconditionFailed
"416": functools.partial(IOError, errno.EINVAL), # InvalidRange
"500": functools.partial(IOError, EREMOTEIO), # InternalError
"501": functools.partial(IOError, errno.ENOSYS), # NotImplemented
"503": functools.partial(IOError, errno.EBUSY), # SlowDown
}
def translate_boto_error(error, message=None, set_cause=True, *args, **kwargs):
"""Convert a ClientError exception into a Python one.
Parameters
----------
error : botocore.exceptions.ClientError
The exception returned by the boto API.
message : str
An error message to use for the returned exception. If not given, the
error message returned by the server is used instead.
set_cause : bool
Whether to set the __cause__ attribute to the previous exception if the
exception is translated.
*args, **kwargs :
Additional arguments to pass to the exception constructor, after the
error message. Useful for passing the filename arguments to ``IOError``.
Returns
-------
An instantiated exception ready to be thrown. If the error code isn't
recognized, an IOError with the original error message is returned.
"""
error_response = getattr(error, "response", None)
if error_response is None:
# non-http error, or response is None:
return error
code = error_response["Error"].get("Code")
if (
code == "PreconditionFailed"
and error_response["Error"].get("Condition", "") == "If-None-Match"
):
constructor = FileExistsError
else:
constructor = ERROR_CODE_TO_EXCEPTION.get(code)
if constructor:
if not message:
message = error_response["Error"].get("Message", str(error))
custom_exc = constructor(message, *args, **kwargs)
else:
# No match found, wrap this in an IOError with the appropriate message.
custom_exc = OSError(errno.EIO, message or str(error), *args)
if set_cause:
custom_exc.__cause__ = error
return custom_exc
|