# -*- coding: utf-8 -*- """ oss2.exceptions ~~~~~~~~~~~~~~ 异常类。 """ import re import xml.etree.ElementTree as ElementTree from xml.parsers import expat from .compat import to_string from .headers import * _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 OSS_SELECT_CLIENT_ERROR_STATUS = -5 class OssError(Exception): def __init__(self, status, headers, body, details): #: HTTP 状态码 self.status = status #: 请求ID,用于跟踪一个OSS请求。提交工单时,最好能够提供请求ID self.request_id = headers.get(OSS_REQUEST_ID, '') #: HTTP响应体(部分) self.body = body #: 详细错误信息,是一个string到string的dict self.details = details #: OSS错误码 self.code = self.details.get('Code', '') #: OSS错误信息 self.message = self.details.get('Message', '') def __str__(self): error = {'status': self.status, OSS_REQUEST_ID : self.request_id, 'details': self.details} return str(error) def _str_with_body(self): error = {'status': self.status, OSS_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, {OSS_REQUEST_ID : request_id}, 'InconsistentError: ' + message, {}) def __str__(self): return self._str_with_body() class OpenApiFormatError(OssError): def __init__(self, message): OssError.__init__(self, OSS_FORMAT_ERROR_STATUS, {}, 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, {OSS_REQUEST_ID : request_id}, '', {'Code': error_code, 'Message': message}) class ServerError(OssError): pass class NotFound(ServerError): status = 404 code = '' class MalformedXml(ServerError): status = 400 code = 'MalformedXML' 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 NotImplemented(ServerError): status = 400 code = 'NotImplemented' class InvalidEncryptionRequest(ServerError): status = 400 code = 'InvalidEncryptionRequest' 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 NoSuchBucketPolicy(NotFound): status = 404 code = 'NoSuchBucketPolicy' class NoSuchInventory(NotFound): status = 404 code = 'NoSuchInventory' 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[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' class NoSuchServerSideEncryptionRule(NotFound): status = 404 code = 'NoSuchServerSideEncryptionRule' class InvalidEncryptionAlgorithmError(ServerError): status = 400 code = 'InvalidEncryptionAlgorithmError' class SelectOperationFailed(ServerError): code = 'SelectOperationFailed' def __init__(self, status, code, message): self.status = status self.code = code self.message = message def __str__(self): error = {'status': self.status, 'code': self.code, 'details': self.message} return str(error) class SelectOperationClientError(OssError): def __init__(self, message, request_id): OssError.__init__(self, OSS_SELECT_CLIENT_ERROR_STATUS, {'x-oss-request-id': request_id}, 'SelectOperationClientError: ' + message, {}) def __str__(self): error = {'x-oss-request-id':self.request_id, 'message': self.message} return str(error) class SignatureDoesNotMatch(ServerError): status = 403 code = 'SignatureDoesNotMatch' class ObjectAlreadyExists(ServerError): status = 400 code = 'ObjectAlreadyExists' class PartNotSequential(ServerError): status = 400 code = 'PartNotSequential' class NoSuchWORMConfiguration(ServerError): status = 404 code = 'NoSuchWORMConfiguration' class WORMConfigurationLocked(ServerError): status = 403 code = 'WORMConfigurationLocked' class InvalidWORMConfiguration(ServerError): status = 400 code = 'InvalidWORMConfiguration' 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 '' not in body or '' not in body: return details m = re.search('(.*)', body) if m: details['Code'] = m.group(1) m = re.search('(.*)', body) if m: details['Message'] = m.group(1) return details