errors.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. # Copyright 2008 Canonical Ltd.
  2. # This file is part of lazr.restfulclient.
  3. #
  4. # lazr.restfulclient is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Lesser General Public License as
  6. # published by the Free Software Foundation, either version 3 of the
  7. # License, or (at your option) any later version.
  8. #
  9. # lazr.restfulclient is distributed in the hope that it will be useful, but
  10. # WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. # Lesser General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Lesser General Public
  15. # License along with lazr.restfulclient. If not, see
  16. # <http://www.gnu.org/licenses/>.
  17. """lazr.restfulclient errors."""
  18. __metaclass__ = type
  19. __all__ = [
  20. "BadRequest",
  21. "Conflict",
  22. "ClientError",
  23. "CredentialsError",
  24. "CredentialsFileError",
  25. "HTTPError",
  26. "MethodNotAllowed",
  27. "NotFound",
  28. "PreconditionFailed",
  29. "RestfulError",
  30. "ResponseError",
  31. "ServerError",
  32. "Unauthorized",
  33. "UnexpectedResponseError",
  34. ]
  35. class RestfulError(Exception):
  36. """Base error for the lazr.restfulclient API library."""
  37. class CredentialsError(RestfulError):
  38. """Base credentials/authentication error."""
  39. class CredentialsFileError(CredentialsError):
  40. """Error in credentials file."""
  41. class ResponseError(RestfulError):
  42. """Error in response."""
  43. def __init__(self, response, content):
  44. RestfulError.__init__(self)
  45. self.response = response
  46. self.content = content
  47. class UnexpectedResponseError(ResponseError):
  48. """An unexpected response was received."""
  49. def __str__(self):
  50. return "%s: %s" % (self.response.status, self.response.reason)
  51. class HTTPError(ResponseError):
  52. """An HTTP non-2xx response code was received."""
  53. def __str__(self):
  54. """Show the error code, response headers, and response body."""
  55. headers = "\n".join(
  56. ["%s: %s" % pair for pair in sorted(self.response.items())]
  57. )
  58. return (
  59. "HTTP Error %s: %s\n"
  60. "Response headers:\n---\n%s\n---\n"
  61. "Response body:\n---\n%s\n---\n"
  62. ) % (self.response.status, self.response.reason, headers, self.content)
  63. class ClientError(HTTPError):
  64. """An exception representing a client-side error."""
  65. class Unauthorized(ClientError):
  66. """An exception representing an authentication failure."""
  67. class NotFound(ClientError):
  68. """An exception representing a nonexistent resource."""
  69. class MethodNotAllowed(ClientError):
  70. """An exception raised when you use an unsupported HTTP method.
  71. This is most likely because you tried to delete a resource that
  72. can't be deleted.
  73. """
  74. class BadRequest(ClientError):
  75. """An exception representing a problem with a client request."""
  76. class Conflict(ClientError):
  77. """An exception representing a conflict with another client."""
  78. class PreconditionFailed(ClientError):
  79. """An exception representing the failure of a conditional PUT/PATCH.
  80. The most likely explanation is that another client changed this
  81. object while you were working on it, and your version of the
  82. object is now out of date.
  83. """
  84. class ServerError(HTTPError):
  85. """An exception representing a server-side error."""
  86. def error_for(response, content):
  87. """Turn an HTTP response into an HTTPError subclass.
  88. :return: None if the response code is 1xx, 2xx or 3xx. Otherwise,
  89. an instance of an appropriate HTTPError subclass (or HTTPError
  90. if nothing else is appropriate.
  91. """
  92. http_errors_by_status_code = {
  93. 400: BadRequest,
  94. 401: Unauthorized,
  95. 404: NotFound,
  96. 405: MethodNotAllowed,
  97. 409: Conflict,
  98. 412: PreconditionFailed,
  99. }
  100. if response.status // 100 <= 3:
  101. # 1xx, 2xx and 3xx are not considered errors.
  102. return None
  103. else:
  104. cls = http_errors_by_status_code.get(response.status, HTTPError)
  105. if cls is HTTPError:
  106. if response.status // 100 == 5:
  107. cls = ServerError
  108. elif response.status // 100 == 4:
  109. cls = ClientError
  110. return cls(response, content)