1#!/usr/bin/env python 2# 3# Copyright 2015 Google Inc. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Exceptions for generated client libraries.""" 18 19 20class Error(Exception): 21 22 """Base class for all exceptions.""" 23 24 25class TypecheckError(Error, TypeError): 26 27 """An object of an incorrect type is provided.""" 28 29 30class NotFoundError(Error): 31 32 """A specified resource could not be found.""" 33 34 35class UserError(Error): 36 37 """Base class for errors related to user input.""" 38 39 40class InvalidDataError(Error): 41 42 """Base class for any invalid data error.""" 43 44 45class CommunicationError(Error): 46 47 """Any communication error talking to an API server.""" 48 49 50class HttpError(CommunicationError): 51 52 """Error making a request. Soon to be HttpError.""" 53 54 def __init__(self, response, content, url, 55 method_config=None, request=None): 56 error_message = HttpError._build_message(response, content, url) 57 super(HttpError, self).__init__(error_message) 58 self.response = response 59 self.content = content 60 self.url = url 61 self.method_config = method_config 62 self.request = request 63 64 def __str__(self): 65 return HttpError._build_message(self.response, self.content, self.url) 66 67 @staticmethod 68 def _build_message(response, content, url): 69 if isinstance(content, bytes): 70 content = content.decode('ascii', 'replace') 71 return 'HttpError accessing <%s>: response: <%s>, content <%s>' % ( 72 url, response, content) 73 74 @property 75 def status_code(self): 76 # TODO(craigcitro): Turn this into something better than a 77 # KeyError if there is no status. 78 return int(self.response['status']) 79 80 @classmethod 81 def FromResponse(cls, http_response, **kwargs): 82 try: 83 status_code = int(http_response.info.get('status')) 84 error_cls = _HTTP_ERRORS.get(status_code, cls) 85 except ValueError: 86 error_cls = cls 87 return error_cls(http_response.info, http_response.content, 88 http_response.request_url, **kwargs) 89 90 91class HttpBadRequestError(HttpError): 92 """HTTP 400 Bad Request.""" 93 94 95class HttpUnauthorizedError(HttpError): 96 """HTTP 401 Unauthorized.""" 97 98 99class HttpForbiddenError(HttpError): 100 """HTTP 403 Forbidden.""" 101 102 103class HttpNotFoundError(HttpError): 104 """HTTP 404 Not Found.""" 105 106 107class HttpConflictError(HttpError): 108 """HTTP 409 Conflict.""" 109 110 111_HTTP_ERRORS = { 112 400: HttpBadRequestError, 113 401: HttpUnauthorizedError, 114 403: HttpForbiddenError, 115 404: HttpNotFoundError, 116 409: HttpConflictError, 117} 118 119 120class InvalidUserInputError(InvalidDataError): 121 122 """User-provided input is invalid.""" 123 124 125class InvalidDataFromServerError(InvalidDataError, CommunicationError): 126 127 """Data received from the server is malformed.""" 128 129 130class BatchError(Error): 131 132 """Error generated while constructing a batch request.""" 133 134 135class ConfigurationError(Error): 136 137 """Base class for configuration errors.""" 138 139 140class GeneratedClientError(Error): 141 142 """The generated client configuration is invalid.""" 143 144 145class ConfigurationValueError(UserError): 146 147 """Some part of the user-specified client configuration is invalid.""" 148 149 150class ResourceUnavailableError(Error): 151 152 """User requested an unavailable resource.""" 153 154 155class CredentialsError(Error): 156 157 """Errors related to invalid credentials.""" 158 159 160class TransferError(CommunicationError): 161 162 """Errors related to transfers.""" 163 164 165class TransferRetryError(TransferError): 166 167 """Retryable errors related to transfers.""" 168 169 170class TransferInvalidError(TransferError): 171 172 """The given transfer is invalid.""" 173 174 175class RequestError(CommunicationError): 176 177 """The request was not successful.""" 178 179 180class RetryAfterError(HttpError): 181 182 """The response contained a retry-after header.""" 183 184 def __init__(self, response, content, url, retry_after, **kwargs): 185 super(RetryAfterError, self).__init__(response, content, url, **kwargs) 186 self.retry_after = int(retry_after) 187 188 @classmethod 189 def FromResponse(cls, http_response, **kwargs): 190 return cls(http_response.info, http_response.content, 191 http_response.request_url, http_response.retry_after, 192 **kwargs) 193 194 195class BadStatusCodeError(HttpError): 196 197 """The request completed but returned a bad status code.""" 198 199 200class NotYetImplementedError(GeneratedClientError): 201 202 """This functionality is not yet implemented.""" 203 204 205class StreamExhausted(Error): 206 207 """Attempted to read more bytes from a stream than were available.""" 208