# -*- coding: utf-8 -*-
"""
oss2.exceptions
~~~~~~~~~~~~~~
Exception classes
"""
import re
import xml.etree.ElementTree as ElementTree
from xml.parsers import expat
from .compat import to_string
_OSS_ERROR_TO_EXCEPTION = {} # populated at end of module
OSS_CLIENT_ERROR_STATUS = -1
OSS_REQUEST_ERROR_STATUS = -2
OSS_INCONSISTENT_ERROR_STATUS = -3
OSS_FORMAT_ERROR_STATUS = -4
[文档]class OssError(Exception):
def __init__(self, status, headers, body, details):
#: HTTP Status code (such as 200)
self.status = status
#: Request ID, which represents a unique OSS request. It is useful when submitting a support ticket.
self.request_id = headers.get('x-oss-request-id', '')
#: HTTP response body
self.body = body
#: The error messages. It is a dict of <string, string>
self.details = details
#: OSS error code
self.code = self.details.get('Code', '')
#: OSS error message
self.message = self.details.get('Message', '')
def __str__(self):
error = {'status': self.status,
'request-id': self.request_id,
'details': self.details}
return str(error)
def _str_with_body(self):
error = {'status': self.status,
'request-id': self.request_id,
'details': self.body}
return str(error)
[文档]class ClientError(OssError):
def __init__(self, message):
OssError.__init__(self, OSS_CLIENT_ERROR_STATUS, {}, 'ClientError: ' + message, {})
def __str__(self):
return self._str_with_body()
[文档]class RequestError(OssError):
def __init__(self, e):
OssError.__init__(self, OSS_REQUEST_ERROR_STATUS, {}, 'RequestError: ' + str(e), {})
self.exception = e
def __str__(self):
return self._str_with_body()
[文档]class InconsistentError(OssError):
def __init__(self, message, request_id=''):
OssError.__init__(self, OSS_INCONSISTENT_ERROR_STATUS, {'x-oss-request-id': request_id}, 'InconsistentError: ' + message, {})
def __str__(self):
return self._str_with_body()
[文档]class OpenApiServerError(OssError):
def __init__(self, status, request_id, message, error_code):
OssError.__init__(self, status, {'x-oss-request-id': request_id}, '', {'Code': error_code, 'Message': message})
[文档]class ServerError(OssError):
pass
[文档]class NotFound(ServerError):
status = 404
code = ''
[文档]class InvalidRequest(ServerError):
status = 400
code = 'InvalidRequest'
[文档]class OperationNotSupported(ServerError):
status = 400
code = 'OperationNotSupported'
[文档]class RestoreAlreadyInProgress(ServerError):
status = 409
code = 'RestoreAlreadyInProgress'
[文档]class InvalidArgument(ServerError):
status = 400
code = 'InvalidArgument'
def __init__(self, status, headers, body, details):
super(InvalidArgument, self).__init__(status, headers, body, details)
self.name = details.get('ArgumentName')
self.value = details.get('ArgumentValue')
[文档]class InvalidDigest(ServerError):
status = 400
code = 'InvalidDigest'
[文档]class InvalidObjectName(ServerError):
status = 400
code = 'InvalidObjectName'
[文档]class NoSuchBucket(NotFound):
status = 404
code = 'NoSuchBucket'
[文档]class NoSuchKey(NotFound):
status = 404
code = 'NoSuchKey'
[文档]class NoSuchUpload(NotFound):
status = 404
code = 'NoSuchUpload'
[文档]class NoSuchWebsite(NotFound):
status = 404
code = 'NoSuchWebsiteConfiguration'
[文档]class NoSuchLifecycle(NotFound):
status = 404
code = 'NoSuchLifecycle'
[文档]class NoSuchCors(NotFound):
status = 404
code = 'NoSuchCORSConfiguration'
[文档]class NoSuchLiveChannel(NotFound):
status = 404
code = 'NoSuchLiveChannel'
[文档]class Conflict(ServerError):
status = 409
code = ''
[文档]class BucketNotEmpty(Conflict):
status = 409
code = 'BucketNotEmpty'
[文档]class PositionNotEqualToLength(Conflict):
status = 409
code = 'PositionNotEqualToLength'
def __init__(self, status, headers, body, details):
super(PositionNotEqualToLength, self).__init__(status, headers, body, details)
self.next_position = int(headers['x-oss-next-append-position'])
[文档]class ObjectNotAppendable(Conflict):
status = 409
code = 'ObjectNotAppendable'
[文档]class ChannelStillLive(Conflict):
status = 409
code = 'ChannelStillLive'
[文档]class LiveChannelDisabled(Conflict):
status = 409
code = 'LiveChannelDisabled'
[文档]class PreconditionFailed(ServerError):
status = 412
code = 'PreconditionFailed'
[文档]class NotModified(ServerError):
status = 304
code = ''
[文档]class AccessDenied(ServerError):
status = 403
code = 'AccessDenied'
[文档]def make_exception(resp):
status = resp.status
headers = resp.headers
body = resp.read(4096)
details = _parse_error_body(body)
code = details.get('Code', '')
try:
klass = _OSS_ERROR_TO_EXCEPTION[(status, code)]
return klass(status, headers, body, details)
except KeyError:
return ServerError(status, headers, body, details)
def _walk_subclasses(klass):
for sub in klass.__subclasses__():
yield sub
for subsub in _walk_subclasses(sub):
yield subsub
for klass in _walk_subclasses(ServerError):
status = getattr(klass, 'status', None)
code = getattr(klass, 'code', None)
if status is not None and code is not None:
_OSS_ERROR_TO_EXCEPTION[(status, code)] = klass
# XML parsing exceptions have changed in Python2.7 and ElementTree 1.3
if hasattr(ElementTree, 'ParseError'):
ElementTreeParseError = (ElementTree.ParseError, expat.ExpatError)
else:
ElementTreeParseError = (expat.ExpatError)
def _parse_error_body(body):
try:
root = ElementTree.fromstring(body)
if root.tag != 'Error':
return {}
details = {}
for child in root:
details[child.tag] = child.text
return details
except ElementTreeParseError:
return _guess_error_details(body)
def _guess_error_details(body):
details = {}
body = to_string(body)
if '<Error>' not in body or '</Error>' not in body:
return details
m = re.search('<Code>(.*)</Code>', body)
if m:
details['Code'] = m.group(1)
m = re.search('<Message>(.*)</Message>', body)
if m:
details['Message'] = m.group(1)
return details