osmapi.errors
Error classes for the OpenStreetMap API.
1""" 2Error classes for the OpenStreetMap API.""" 3 4import warnings 5from typing import Any 6 7 8class OsmApiError(Exception): 9 """ 10 General OsmApi error class to provide a superclass for all other errors 11 """ 12 13 14class MaximumRetryLimitReachedError(OsmApiError): 15 """ 16 Error when the maximum amount of retries is reached and we have to give up 17 """ 18 19 20class AuthenticationMissingError(OsmApiError): 21 """ 22 Error when a request requires authentication, but no session was provided 23 that could carry credentials. 24 25 Pass an authenticated `requests.Session` (e.g. an OAuth 2.0 session, see 26 the [README](https://github.com/metaodi/osmapi#oauth-authentication)) to 27 `OsmApi` to make authenticated requests. 28 29 This error is raised before the request is sent, and only when `OsmApi` 30 created the http session itself — in that case there is no way for the 31 request to be authenticated. A session that was passed in is never 32 second-guessed (it can carry a token in `session.auth`, in an 33 `Authorization` header, in a transport adapter, or add it per request), 34 so a missing or invalid authorization on such a session is reported by 35 the API as `OsmApi.UnauthorizedApiError` (HTTP 401) instead. 36 37 Before version 6.0 this error was called `UsernamePasswordMissingError`. 38 That name still works, but it is deprecated and will be removed in 39 version 7.0. 40 """ 41 42 pass 43 44 45class NoChangesetOpenError(OsmApiError): 46 """ 47 Error when an operation requires an open changeset, but currently 48 no changeset _is_ open 49 """ 50 51 pass 52 53 54class ChangesetAlreadyOpenError(OsmApiError): 55 """ 56 Error when a user tries to open a changeset when there is already 57 an open changeset 58 """ 59 60 pass 61 62 63class OsmTypeAlreadyExistsError(OsmApiError): 64 """ 65 Error when a user tries to create an object that already exsits 66 """ 67 68 pass 69 70 71class XmlResponseInvalidError(OsmApiError): 72 """ 73 Error if the XML response from the OpenStreetMap API is invalid 74 """ 75 76 77class ApiError(OsmApiError): 78 """ 79 Error class, is thrown when an API request fails 80 """ 81 82 def __init__(self, status: int, reason: str, payload: Any) -> None: 83 self.status = status 84 """HTTP error code""" 85 86 self.reason = reason 87 """Error message""" 88 89 self.payload = payload 90 """Payload of API when this error occured""" 91 92 @property 93 def payload_str(self) -> str: 94 """ 95 The payload decoded as text. 96 97 `payload` is usually the raw `bytes` body of the response; use this 98 when the payload needs to be matched against or shown as a string. 99 """ 100 if isinstance(self.payload, bytes): 101 return self.payload.decode("utf-8", errors="replace") 102 return str(self.payload) 103 104 def __str__(self) -> str: 105 return f"Request failed: {self.status} - {self.reason} - {self.payload}" 106 107 108class UnauthorizedApiError(ApiError): 109 """ 110 Error when the API returned an Unauthorized error, 111 e.g. when the provided OAuth token is expired 112 """ 113 114 pass 115 116 117class AlreadySubscribedApiError(ApiError): 118 """ 119 Error when a user tries to subscribe to a changeset 120 that she is already subscribed to 121 """ 122 123 pass 124 125 126class NotSubscribedApiError(ApiError): 127 """ 128 Error when user tries to unsubscribe from a changeset 129 that he is not subscribed to 130 """ 131 132 pass 133 134 135class ElementDeletedApiError(ApiError): 136 """ 137 Error when the requested element is deleted 138 """ 139 140 pass 141 142 143class ElementNotFoundApiError(ApiError): 144 """ 145 Error if the the requested element was not found 146 """ 147 148 149class ResponseEmptyApiError(ApiError): 150 """ 151 Error when the response to the request is empty 152 """ 153 154 pass 155 156 157class ChangesetClosedApiError(ApiError): 158 """ 159 Error if the the changeset in question has already been closed 160 """ 161 162 163class NoteAlreadyClosedApiError(ApiError): 164 """ 165 Error if the the note in question has already been closed 166 """ 167 168 169class VersionMismatchApiError(ApiError): 170 """ 171 Error if the provided version does not match the database version 172 of the element 173 """ 174 175 176class PreconditionFailedApiError(ApiError): 177 """ 178 Error if the precondition of the operation was not met: 179 - When a way has nodes that do not exist or are not visible 180 - When a relation has elements that do not exist or are not visible 181 - When a node/way/relation is still used in a way/relation 182 """ 183 184 185class TimeoutApiError(ApiError): 186 """ 187 Error if the http request ran into a timeout 188 """ 189 190 191class ConnectionApiError(ApiError): 192 """ 193 Error if there was a network error (e.g. DNS failure, refused connection) 194 while connecting to the remote server. 195 """ 196 197 198DEPRECATED_ERROR_NAMES = { 199 "UsernamePasswordMissingError": "AuthenticationMissingError", 200} 201""" 202Error names that were renamed, mapped to their replacement. 203 204The old names still resolve to the new class (so `except` clauses keep 205working), but emit a `DeprecationWarning`. They will be removed in 206version 7.0. 207""" 208 209 210def resolve_deprecated_name(name: str) -> Any: 211 """ 212 Return the error class a deprecated name refers to, with a warning. 213 214 Raises `AttributeError` if `name` is not a deprecated error name. The 215 `DeprecationWarning` is reported against the caller of the module-level 216 `__getattr__` that calls this, i.e. the code using the old name. 217 """ 218 new_name = DEPRECATED_ERROR_NAMES.get(name) 219 if not new_name: 220 raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 221 warnings.warn( 222 f"{name} has been renamed to {new_name}, " 223 f"the old name is deprecated and will be removed in osmapi 7.0", 224 DeprecationWarning, 225 stacklevel=3, 226 ) 227 return globals()[new_name] 228 229 230def __getattr__(name: str) -> Any: 231 """ 232 Resolve a deprecated error name to its replacement (see PEP 562). 233 234 Accessing `osmapi.errors.UsernamePasswordMissingError` returns 235 `AuthenticationMissingError` and emits a `DeprecationWarning`. 236 """ 237 return resolve_deprecated_name(name)
9class OsmApiError(Exception): 10 """ 11 General OsmApi error class to provide a superclass for all other errors 12 """
General OsmApi error class to provide a superclass for all other errors
15class MaximumRetryLimitReachedError(OsmApiError): 16 """ 17 Error when the maximum amount of retries is reached and we have to give up 18 """
Error when the maximum amount of retries is reached and we have to give up
21class AuthenticationMissingError(OsmApiError): 22 """ 23 Error when a request requires authentication, but no session was provided 24 that could carry credentials. 25 26 Pass an authenticated `requests.Session` (e.g. an OAuth 2.0 session, see 27 the [README](https://github.com/metaodi/osmapi#oauth-authentication)) to 28 `OsmApi` to make authenticated requests. 29 30 This error is raised before the request is sent, and only when `OsmApi` 31 created the http session itself — in that case there is no way for the 32 request to be authenticated. A session that was passed in is never 33 second-guessed (it can carry a token in `session.auth`, in an 34 `Authorization` header, in a transport adapter, or add it per request), 35 so a missing or invalid authorization on such a session is reported by 36 the API as `OsmApi.UnauthorizedApiError` (HTTP 401) instead. 37 38 Before version 6.0 this error was called `UsernamePasswordMissingError`. 39 That name still works, but it is deprecated and will be removed in 40 version 7.0. 41 """ 42 43 pass
Error when a request requires authentication, but no session was provided that could carry credentials.
Pass an authenticated requests.Session (e.g. an OAuth 2.0 session, see
the README) to
OsmApi to make authenticated requests.
This error is raised before the request is sent, and only when OsmApi
created the http session itself — in that case there is no way for the
request to be authenticated. A session that was passed in is never
second-guessed (it can carry a token in session.auth, in an
Authorization header, in a transport adapter, or add it per request),
so a missing or invalid authorization on such a session is reported by
the API as OsmApi.UnauthorizedApiError (HTTP 401) instead.
Before version 6.0 this error was called UsernamePasswordMissingError.
That name still works, but it is deprecated and will be removed in
version 7.0.
46class NoChangesetOpenError(OsmApiError): 47 """ 48 Error when an operation requires an open changeset, but currently 49 no changeset _is_ open 50 """ 51 52 pass
Error when an operation requires an open changeset, but currently no changeset _is_ open
55class ChangesetAlreadyOpenError(OsmApiError): 56 """ 57 Error when a user tries to open a changeset when there is already 58 an open changeset 59 """ 60 61 pass
Error when a user tries to open a changeset when there is already an open changeset
64class OsmTypeAlreadyExistsError(OsmApiError): 65 """ 66 Error when a user tries to create an object that already exsits 67 """ 68 69 pass
Error when a user tries to create an object that already exsits
72class XmlResponseInvalidError(OsmApiError): 73 """ 74 Error if the XML response from the OpenStreetMap API is invalid 75 """
Error if the XML response from the OpenStreetMap API is invalid
78class ApiError(OsmApiError): 79 """ 80 Error class, is thrown when an API request fails 81 """ 82 83 def __init__(self, status: int, reason: str, payload: Any) -> None: 84 self.status = status 85 """HTTP error code""" 86 87 self.reason = reason 88 """Error message""" 89 90 self.payload = payload 91 """Payload of API when this error occured""" 92 93 @property 94 def payload_str(self) -> str: 95 """ 96 The payload decoded as text. 97 98 `payload` is usually the raw `bytes` body of the response; use this 99 when the payload needs to be matched against or shown as a string. 100 """ 101 if isinstance(self.payload, bytes): 102 return self.payload.decode("utf-8", errors="replace") 103 return str(self.payload) 104 105 def __str__(self) -> str: 106 return f"Request failed: {self.status} - {self.reason} - {self.payload}"
Error class, is thrown when an API request fails
93 @property 94 def payload_str(self) -> str: 95 """ 96 The payload decoded as text. 97 98 `payload` is usually the raw `bytes` body of the response; use this 99 when the payload needs to be matched against or shown as a string. 100 """ 101 if isinstance(self.payload, bytes): 102 return self.payload.decode("utf-8", errors="replace") 103 return str(self.payload)
The payload decoded as text.
payload is usually the raw bytes body of the response; use this
when the payload needs to be matched against or shown as a string.
118class AlreadySubscribedApiError(ApiError): 119 """ 120 Error when a user tries to subscribe to a changeset 121 that she is already subscribed to 122 """ 123 124 pass
Error when a user tries to subscribe to a changeset that she is already subscribed to
127class NotSubscribedApiError(ApiError): 128 """ 129 Error when user tries to unsubscribe from a changeset 130 that he is not subscribed to 131 """ 132 133 pass
Error when user tries to unsubscribe from a changeset that he is not subscribed to
136class ElementDeletedApiError(ApiError): 137 """ 138 Error when the requested element is deleted 139 """ 140 141 pass
Error when the requested element is deleted
144class ElementNotFoundApiError(ApiError): 145 """ 146 Error if the the requested element was not found 147 """
Error if the the requested element was not found
150class ResponseEmptyApiError(ApiError): 151 """ 152 Error when the response to the request is empty 153 """ 154 155 pass
Error when the response to the request is empty
158class ChangesetClosedApiError(ApiError): 159 """ 160 Error if the the changeset in question has already been closed 161 """
Error if the the changeset in question has already been closed
164class NoteAlreadyClosedApiError(ApiError): 165 """ 166 Error if the the note in question has already been closed 167 """
Error if the the note in question has already been closed
170class VersionMismatchApiError(ApiError): 171 """ 172 Error if the provided version does not match the database version 173 of the element 174 """
Error if the provided version does not match the database version of the element
177class PreconditionFailedApiError(ApiError): 178 """ 179 Error if the precondition of the operation was not met: 180 - When a way has nodes that do not exist or are not visible 181 - When a relation has elements that do not exist or are not visible 182 - When a node/way/relation is still used in a way/relation 183 """
Error if the precondition of the operation was not met:
- When a way has nodes that do not exist or are not visible
- When a relation has elements that do not exist or are not visible
- When a node/way/relation is still used in a way/relation
186class TimeoutApiError(ApiError): 187 """ 188 Error if the http request ran into a timeout 189 """
Error if the http request ran into a timeout
192class ConnectionApiError(ApiError): 193 """ 194 Error if there was a network error (e.g. DNS failure, refused connection) 195 while connecting to the remote server. 196 """
Error if there was a network error (e.g. DNS failure, refused connection) while connecting to the remote server.
Error names that were renamed, mapped to their replacement.
The old names still resolve to the new class (so except clauses keep
working), but emit a DeprecationWarning. They will be removed in
version 7.0.
211def resolve_deprecated_name(name: str) -> Any: 212 """ 213 Return the error class a deprecated name refers to, with a warning. 214 215 Raises `AttributeError` if `name` is not a deprecated error name. The 216 `DeprecationWarning` is reported against the caller of the module-level 217 `__getattr__` that calls this, i.e. the code using the old name. 218 """ 219 new_name = DEPRECATED_ERROR_NAMES.get(name) 220 if not new_name: 221 raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 222 warnings.warn( 223 f"{name} has been renamed to {new_name}, " 224 f"the old name is deprecated and will be removed in osmapi 7.0", 225 DeprecationWarning, 226 stacklevel=3, 227 ) 228 return globals()[new_name]
Return the error class a deprecated name refers to, with a warning.
Raises AttributeError if name is not a deprecated error name. The
DeprecationWarning is reported against the caller of the module-level
__getattr__ that calls this, i.e. the code using the old name.