exceptions.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. # -*- coding: utf-8 -*-
  2. """
  3. oss2.exceptions
  4. ~~~~~~~~~~~~~~
  5. 异常类。
  6. """
  7. import re
  8. import xml.etree.ElementTree as ElementTree
  9. from xml.parsers import expat
  10. from .compat import to_string
  11. from .headers import *
  12. _OSS_ERROR_TO_EXCEPTION = {} # populated at end of module
  13. OSS_CLIENT_ERROR_STATUS = -1
  14. OSS_REQUEST_ERROR_STATUS = -2
  15. OSS_INCONSISTENT_ERROR_STATUS = -3
  16. OSS_FORMAT_ERROR_STATUS = -4
  17. OSS_SELECT_CLIENT_ERROR_STATUS = -5
  18. class OssError(Exception):
  19. def __init__(self, status, headers, body, details):
  20. #: HTTP 状态码
  21. self.status = status
  22. #: 请求ID,用于跟踪一个OSS请求。提交工单时,最好能够提供请求ID
  23. self.request_id = headers.get(OSS_REQUEST_ID, '')
  24. #: HTTP响应体(部分)
  25. self.body = body
  26. #: 详细错误信息,是一个string到string的dict
  27. self.details = details
  28. #: OSS错误码
  29. self.code = self.details.get('Code', '')
  30. #: OSS错误信息
  31. self.message = self.details.get('Message', '')
  32. def __str__(self):
  33. error = {'status': self.status,
  34. OSS_REQUEST_ID : self.request_id,
  35. 'details': self.details}
  36. return str(error)
  37. def _str_with_body(self):
  38. error = {'status': self.status,
  39. OSS_REQUEST_ID : self.request_id,
  40. 'details': self.body}
  41. return str(error)
  42. class ClientError(OssError):
  43. def __init__(self, message):
  44. OssError.__init__(self, OSS_CLIENT_ERROR_STATUS, {}, 'ClientError: ' + message, {})
  45. def __str__(self):
  46. return self._str_with_body()
  47. class RequestError(OssError):
  48. def __init__(self, e):
  49. OssError.__init__(self, OSS_REQUEST_ERROR_STATUS, {}, 'RequestError: ' + str(e), {})
  50. self.exception = e
  51. def __str__(self):
  52. return self._str_with_body()
  53. class InconsistentError(OssError):
  54. def __init__(self, message, request_id=''):
  55. OssError.__init__(self, OSS_INCONSISTENT_ERROR_STATUS, {OSS_REQUEST_ID : request_id}, 'InconsistentError: ' + message, {})
  56. def __str__(self):
  57. return self._str_with_body()
  58. class OpenApiFormatError(OssError):
  59. def __init__(self, message):
  60. OssError.__init__(self, OSS_FORMAT_ERROR_STATUS, {}, message, {})
  61. def __str__(self):
  62. return self._str_with_body()
  63. class OpenApiServerError(OssError):
  64. def __init__(self, status, request_id, message, error_code):
  65. OssError.__init__(self, status, {OSS_REQUEST_ID : request_id}, '', {'Code': error_code, 'Message': message})
  66. class ServerError(OssError):
  67. pass
  68. class NotFound(ServerError):
  69. status = 404
  70. code = ''
  71. class MalformedXml(ServerError):
  72. status = 400
  73. code = 'MalformedXML'
  74. class InvalidRequest(ServerError):
  75. status = 400
  76. code = 'InvalidRequest'
  77. class OperationNotSupported(ServerError):
  78. status = 400
  79. code = 'OperationNotSupported'
  80. class RestoreAlreadyInProgress(ServerError):
  81. status = 409
  82. code = 'RestoreAlreadyInProgress'
  83. class InvalidArgument(ServerError):
  84. status = 400
  85. code = 'InvalidArgument'
  86. def __init__(self, status, headers, body, details):
  87. super(InvalidArgument, self).__init__(status, headers, body, details)
  88. self.name = details.get('ArgumentName')
  89. self.value = details.get('ArgumentValue')
  90. class InvalidDigest(ServerError):
  91. status = 400
  92. code = 'InvalidDigest'
  93. class InvalidObjectName(ServerError):
  94. status = 400
  95. code = 'InvalidObjectName'
  96. class NotImplemented(ServerError):
  97. status = 400
  98. code = 'NotImplemented'
  99. class InvalidEncryptionRequest(ServerError):
  100. status = 400
  101. code = 'InvalidEncryptionRequest'
  102. class NoSuchBucket(NotFound):
  103. status = 404
  104. code = 'NoSuchBucket'
  105. class NoSuchKey(NotFound):
  106. status = 404
  107. code = 'NoSuchKey'
  108. class NoSuchUpload(NotFound):
  109. status = 404
  110. code = 'NoSuchUpload'
  111. class NoSuchWebsite(NotFound):
  112. status = 404
  113. code = 'NoSuchWebsiteConfiguration'
  114. class NoSuchLifecycle(NotFound):
  115. status = 404
  116. code = 'NoSuchLifecycle'
  117. class NoSuchCors(NotFound):
  118. status = 404
  119. code = 'NoSuchCORSConfiguration'
  120. class NoSuchLiveChannel(NotFound):
  121. status = 404
  122. code = 'NoSuchLiveChannel'
  123. class NoSuchBucketPolicy(NotFound):
  124. status = 404
  125. code = 'NoSuchBucketPolicy'
  126. class NoSuchInventory(NotFound):
  127. status = 404
  128. code = 'NoSuchInventory'
  129. class Conflict(ServerError):
  130. status = 409
  131. code = ''
  132. class BucketNotEmpty(Conflict):
  133. status = 409
  134. code = 'BucketNotEmpty'
  135. class PositionNotEqualToLength(Conflict):
  136. status = 409
  137. code = 'PositionNotEqualToLength'
  138. def __init__(self, status, headers, body, details):
  139. super(PositionNotEqualToLength, self).__init__(status, headers, body, details)
  140. self.next_position = int(headers[OSS_NEXT_APPEND_POSITION])
  141. class ObjectNotAppendable(Conflict):
  142. status = 409
  143. code = 'ObjectNotAppendable'
  144. class ChannelStillLive(Conflict):
  145. status = 409
  146. code = 'ChannelStillLive'
  147. class LiveChannelDisabled(Conflict):
  148. status = 409
  149. code = 'LiveChannelDisabled'
  150. class PreconditionFailed(ServerError):
  151. status = 412
  152. code = 'PreconditionFailed'
  153. class NotModified(ServerError):
  154. status = 304
  155. code = ''
  156. class AccessDenied(ServerError):
  157. status = 403
  158. code = 'AccessDenied'
  159. class NoSuchServerSideEncryptionRule(NotFound):
  160. status = 404
  161. code = 'NoSuchServerSideEncryptionRule'
  162. class InvalidEncryptionAlgorithmError(ServerError):
  163. status = 400
  164. code = 'InvalidEncryptionAlgorithmError'
  165. class SelectOperationFailed(ServerError):
  166. code = 'SelectOperationFailed'
  167. def __init__(self, status, code, message):
  168. self.status = status
  169. self.code = code
  170. self.message = message
  171. def __str__(self):
  172. error = {'status': self.status,
  173. 'code': self.code,
  174. 'details': self.message}
  175. return str(error)
  176. class SelectOperationClientError(OssError):
  177. def __init__(self, message, request_id):
  178. OssError.__init__(self, OSS_SELECT_CLIENT_ERROR_STATUS, {'x-oss-request-id': request_id}, 'SelectOperationClientError: ' + message, {})
  179. def __str__(self):
  180. error = {'x-oss-request-id':self.request_id,
  181. 'message': self.message}
  182. return str(error)
  183. class SignatureDoesNotMatch(ServerError):
  184. status = 403
  185. code = 'SignatureDoesNotMatch'
  186. class ObjectAlreadyExists(ServerError):
  187. status = 400
  188. code = 'ObjectAlreadyExists'
  189. class PartNotSequential(ServerError):
  190. status = 400
  191. code = 'PartNotSequential'
  192. class NoSuchWORMConfiguration(ServerError):
  193. status = 404
  194. code = 'NoSuchWORMConfiguration'
  195. class WORMConfigurationLocked(ServerError):
  196. status = 403
  197. code = 'WORMConfigurationLocked'
  198. class InvalidWORMConfiguration(ServerError):
  199. status = 400
  200. code = 'InvalidWORMConfiguration'
  201. def make_exception(resp):
  202. status = resp.status
  203. headers = resp.headers
  204. body = resp.read(4096)
  205. details = _parse_error_body(body)
  206. code = details.get('Code', '')
  207. try:
  208. klass = _OSS_ERROR_TO_EXCEPTION[(status, code)]
  209. return klass(status, headers, body, details)
  210. except KeyError:
  211. return ServerError(status, headers, body, details)
  212. def _walk_subclasses(klass):
  213. for sub in klass.__subclasses__():
  214. yield sub
  215. for subsub in _walk_subclasses(sub):
  216. yield subsub
  217. for klass in _walk_subclasses(ServerError):
  218. status = getattr(klass, 'status', None)
  219. code = getattr(klass, 'code', None)
  220. if status is not None and code is not None:
  221. _OSS_ERROR_TO_EXCEPTION[(status, code)] = klass
  222. # XML parsing exceptions have changed in Python2.7 and ElementTree 1.3
  223. if hasattr(ElementTree, 'ParseError'):
  224. ElementTreeParseError = (ElementTree.ParseError, expat.ExpatError)
  225. else:
  226. ElementTreeParseError = (expat.ExpatError)
  227. def _parse_error_body(body):
  228. try:
  229. root = ElementTree.fromstring(body)
  230. if root.tag != 'Error':
  231. return {}
  232. details = {}
  233. for child in root:
  234. details[child.tag] = child.text
  235. return details
  236. except ElementTreeParseError:
  237. return _guess_error_details(body)
  238. def _guess_error_details(body):
  239. details = {}
  240. body = to_string(body)
  241. if '<Error>' not in body or '</Error>' not in body:
  242. return details
  243. m = re.search('<Code>(.*)</Code>', body)
  244. if m:
  245. details['Code'] = m.group(1)
  246. m = re.search('<Message>(.*)</Message>', body)
  247. if m:
  248. details['Message'] = m.group(1)
  249. return details