1# -*- coding: utf-8 -*- 2 3## Amazon S3 manager - Exceptions library 4## Author: Michal Ludvig <michal@logix.cz> 5## http://www.logix.cz/michal 6## License: GPL Version 2 7## Copyright: TGRMN Software and contributors 8 9from __future__ import absolute_import 10 11from logging import debug, error 12import sys 13import S3.BaseUtils 14import S3.Utils 15from . import ExitCodes 16 17if sys.version_info >= (3, 0): 18 PY3 = True 19 # In python 3, unicode -> str, and str -> bytes 20 unicode = str 21else: 22 PY3 = False 23 24## External exceptions 25 26from ssl import SSLError as S3SSLError 27 28try: 29 from ssl import CertificateError as S3SSLCertificateError 30except ImportError: 31 class S3SSLCertificateError(Exception): 32 pass 33 34 35try: 36 from xml.etree.ElementTree import ParseError as XmlParseError 37except ImportError: 38 # ParseError was only added in python2.7, before ET was raising ExpatError 39 from xml.parsers.expat import ExpatError as XmlParseError 40 41 42## s3cmd exceptions 43 44class S3Exception(Exception): 45 def __init__(self, message=""): 46 self.message = S3.Utils.unicodise(message) 47 48 def __str__(self): 49 ## Don't return self.message directly because 50 ## __unicode__() method could be overridden in subclasses! 51 if PY3: 52 return self.__unicode__() 53 else: 54 return S3.Utils.deunicodise(self.__unicode__()) 55 56 def __unicode__(self): 57 return self.message 58 59 ## (Base)Exception.message has been deprecated in Python 2.6 60 def _get_message(self): 61 return self._message 62 63 def _set_message(self, message): 64 self._message = message 65 message = property(_get_message, _set_message) 66 67 68class S3Error (S3Exception): 69 def __init__(self, response): 70 self.status = response["status"] 71 self.reason = response["reason"] 72 self.info = { 73 "Code": "", 74 "Message": "", 75 "Resource": "" 76 } 77 debug("S3Error: %s (%s)" % (self.status, self.reason)) 78 if "headers" in response: 79 for header in response["headers"]: 80 debug("HttpHeader: %s: %s" % (header, response["headers"][header])) 81 if "data" in response and response["data"]: 82 try: 83 tree = S3.BaseUtils.getTreeFromXml(response["data"]) 84 except XmlParseError: 85 debug("Not an XML response") 86 else: 87 try: 88 self.info.update(self.parse_error_xml(tree)) 89 except Exception as e: 90 error("Error parsing xml: %s. ErrorXML: %s" % (e, response["data"])) 91 92 self.code = self.info["Code"] 93 self.message = self.info["Message"] 94 self.resource = self.info["Resource"] 95 96 def __unicode__(self): 97 retval = u"%d " % (self.status) 98 retval += (u"(%s)" % ("Code" in self.info and self.info["Code"] or self.reason)) 99 error_msg = self.info.get("Message") 100 if error_msg: 101 retval += (u": %s" % error_msg) 102 return retval 103 104 def get_error_code(self): 105 if self.status in [301, 307]: 106 return ExitCodes.EX_SERVERMOVED 107 elif self.status in [400, 405, 411, 416, 417, 501, 504]: 108 return ExitCodes.EX_SERVERERROR 109 elif self.status == 403: 110 return ExitCodes.EX_ACCESSDENIED 111 elif self.status == 404: 112 return ExitCodes.EX_NOTFOUND 113 elif self.status == 409: 114 return ExitCodes.EX_CONFLICT 115 elif self.status == 412: 116 return ExitCodes.EX_PRECONDITION 117 elif self.status == 500: 118 return ExitCodes.EX_SOFTWARE 119 elif self.status in [429, 503]: 120 return ExitCodes.EX_SERVICE 121 else: 122 return ExitCodes.EX_SOFTWARE 123 124 @staticmethod 125 def parse_error_xml(tree): 126 info = {} 127 error_node = tree 128 if not error_node.tag == "Error": 129 error_node = tree.find(".//Error") 130 if error_node is not None: 131 for child in error_node: 132 if child.text != "": 133 debug("ErrorXML: " + child.tag + ": " + repr(child.text)) 134 info[child.tag] = child.text 135 else: 136 raise S3ResponseError("Malformed error XML returned from remote server.") 137 return info 138 139 140class CloudFrontError(S3Error): 141 pass 142 143class S3UploadError(S3Exception): 144 pass 145 146class S3DownloadError(S3Exception): 147 pass 148 149class S3RequestError(S3Exception): 150 pass 151 152class S3ResponseError(S3Exception): 153 pass 154 155class InvalidFileError(S3Exception): 156 pass 157 158class ParameterError(S3Exception): 159 pass 160 161# vim:et:ts=4:sts=4:ai 162