File: errors.py

package info (click to toggle)
python-s3fs 2026.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 620 kB
  • sloc: python: 6,138; makefile: 190
file content (162 lines) | stat: -rw-r--r-- 7,961 bytes parent folder | download
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