Coverage for aiocoap/error.py: 84%
123 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-08-01 17:22 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-08-01 17:22 +0000
1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
2#
3# SPDX-License-Identifier: MIT
5"""
6Common errors for the aiocoap library
7"""
9import warnings
10import abc
11import errno
13from .numbers import codes
14from . import util
17class Error(Exception):
18 """
19 Base exception for all exceptions that indicate a failed request
20 """
23class RenderableError(Error, metaclass=abc.ABCMeta):
24 """
25 Exception that can meaningfully be represented in a CoAP response
26 """
28 @abc.abstractmethod
29 def to_message(self):
30 """Create a CoAP message that should be sent when this exception is
31 rendered"""
34class ResponseWrappingError(Error):
35 """
36 An exception that is raised due to an unsuccessful but received response.
38 A better relationship with :mod:`.numbers.codes` should be worked out to do
39 ``except UnsupportedMediaType`` (similar to the various ``OSError``
40 subclasses).
41 """
43 def __init__(self, coapmessage):
44 self.coapmessage = coapmessage
46 def to_message(self):
47 return self.coapmessage
49 def __repr__(self):
50 return "<%s: %s %r>" % (
51 type(self).__name__,
52 self.coapmessage.code,
53 self.coapmessage.payload,
54 )
57class ConstructionRenderableError(RenderableError):
58 """
59 RenderableError that is constructed from class attributes :attr:`code` and
60 :attr:`message` (where the can be overridden in the constructor).
61 """
63 def __init__(self, message=None):
64 if message is not None:
65 self.message = message
67 def to_message(self):
68 from .message import Message
70 return Message(code=self.code, payload=self.message.encode("utf8"))
72 code = codes.INTERNAL_SERVER_ERROR #: Code assigned to messages built from it
73 message = "" #: Text sent in the built message's payload
76# This block is code-generated to make the types available to static checkers.
77# The __debug__ check below ensures that it stays up to date.
78class BadRequest(ConstructionRenderableError):
79 code = codes.BAD_REQUEST
82class Unauthorized(ConstructionRenderableError):
83 code = codes.UNAUTHORIZED
86class BadOption(ConstructionRenderableError):
87 code = codes.BAD_OPTION
90class Forbidden(ConstructionRenderableError):
91 code = codes.FORBIDDEN
94class NotFound(ConstructionRenderableError):
95 code = codes.NOT_FOUND
98class MethodNotAllowed(ConstructionRenderableError):
99 code = codes.METHOD_NOT_ALLOWED
102class NotAcceptable(ConstructionRenderableError):
103 code = codes.NOT_ACCEPTABLE
106class RequestEntityIncomplete(ConstructionRenderableError):
107 code = codes.REQUEST_ENTITY_INCOMPLETE
110class Conflict(ConstructionRenderableError):
111 code = codes.CONFLICT
114class PreconditionFailed(ConstructionRenderableError):
115 code = codes.PRECONDITION_FAILED
118class RequestEntityTooLarge(ConstructionRenderableError):
119 code = codes.REQUEST_ENTITY_TOO_LARGE
122class UnsupportedContentFormat(ConstructionRenderableError):
123 code = codes.UNSUPPORTED_CONTENT_FORMAT
126class UnprocessableEntity(ConstructionRenderableError):
127 code = codes.UNPROCESSABLE_ENTITY
130class TooManyRequests(ConstructionRenderableError):
131 code = codes.TOO_MANY_REQUESTS
134class InternalServerError(ConstructionRenderableError):
135 code = codes.INTERNAL_SERVER_ERROR
138class NotImplemented(ConstructionRenderableError):
139 code = codes.NOT_IMPLEMENTED
142class BadGateway(ConstructionRenderableError):
143 code = codes.BAD_GATEWAY
146class ServiceUnavailable(ConstructionRenderableError):
147 code = codes.SERVICE_UNAVAILABLE
150class GatewayTimeout(ConstructionRenderableError):
151 code = codes.GATEWAY_TIMEOUT
154class ProxyingNotSupported(ConstructionRenderableError):
155 code = codes.PROXYING_NOT_SUPPORTED
158class HopLimitReached(ConstructionRenderableError):
159 code = codes.HOP_LIMIT_REACHED
162if __debug__:
163 _missing_codes = False
164 _full_code = ""
165 for code in codes.Code:
166 if code.is_successful() or not code.is_response():
167 continue
168 classname = "".join(w.title() for w in code.name.split("_"))
169 _full_code += f"""
170class {classname}(ConstructionRenderableError):
171 code = codes.{code.name}"""
172 if classname not in locals():
173 warnings.warn(f"Missing exception type: f{classname}")
174 _missing_codes = True
175 continue
176 if locals()[classname].code != code:
177 warnings.warn(
178 f"Mismatched code for {classname}: Should be {code}, is {locals()[classname].code}"
179 )
180 _missing_codes = True
181 continue
182 if _missing_codes:
183 warnings.warn(
184 "Generated exception list is out of sync, should be:\n" + _full_code
185 )
187# More detailed versions of code based errors
190class NoResource(NotFound):
191 """
192 Raised when resource is not found.
193 """
195 message = "Error: Resource not found!"
197 def __init__(self):
198 warnings.warn(
199 "NoResource is deprecated in favor of NotFound",
200 DeprecationWarning,
201 stacklevel=2,
202 )
205class UnallowedMethod(MethodNotAllowed):
206 """
207 Raised by a resource when request method is understood by the server
208 but not allowed for that particular resource.
209 """
211 message = "Error: Method not allowed!"
214class UnsupportedMethod(MethodNotAllowed):
215 """
216 Raised when request method is not understood by the server at all.
217 """
219 message = "Error: Method not recognized!"
222class NetworkError(Error):
223 """Base class for all "something went wrong with name resolution, sending
224 or receiving packages".
226 Errors of these kinds are raised towards client callers when things went
227 wrong network-side, or at context creation. They are often raised from
228 socket.gaierror or similar classes, but these are wrapped in order to make
229 catching them possible independently of the underlying transport."""
231 def extra_help(self):
232 """Information printed at aiocoap-client or similar occasions when the
233 error message itself may be insufficient to point the user in the right
234 direction"""
235 if isinstance(self.__cause__, OSError):
236 if self.__cause__.errno == errno.ECONNREFUSED:
237 # seen trying to reach any used address with the port closed
238 return "The remote host could be reached, but reported that the requested port is not open. Check whether a CoAP server is running at the address, or whether it is running on a different port."
239 if self.__cause__.errno == errno.EHOSTUNREACH:
240 # seen trying to reach any unused local address
241 return "No way of contacting the remote host could be found. This could be because a host on the local network is offline or firewalled. Tools for debugging in the next step could be ping or traceroute."
242 if self.__cause__.errno == errno.ENETUNREACH:
243 # seen trying to reach an IPv6 host through an IP literal from a v4-only system, or trying to reach 2001:db8::1
244 return "No way of contacting the remote network could be found. This may be due to lack of IPv6 connectivity, lack of a concrete route (eg. trying to reach a private use network which there is no route to). Tools for debugging in the next step could be ping or traceroute."
245 if self.__cause__.errno == errno.EACCES:
246 # seen trying to reach the broadcast address of a local network
247 return "The operating system refused to send the request. For example, this can occur when attempting to send broadcast requests instead of multicast requests."
250class ResolutionError(NetworkError):
251 """Resolving the host component of a URI to a usable transport address was
252 not possible"""
255class MessageError(NetworkError):
256 """Received an error from the remote on the CoAP message level (typically a
257 RST)"""
260class RemoteServerShutdown(NetworkError):
261 """The peer a request was sent to in a stateful connection closed the
262 connection around the time the request was sent"""
265class TimeoutError(NetworkError):
266 """Base for all timeout-ish errors.
268 Like NetworkError, receiving this alone does not indicate whether the
269 request may have reached the server or not.
270 """
272 def extra_help(self):
273 return "Neither a response nor an error was received. This can have a wide range of causes, from the address being wrong to the server being stuck."
276class ConRetransmitsExceeded(TimeoutError):
277 """A transport that retransmits CON messages has failed to obtain a response
278 within its retransmission timeout.
280 When this is raised in a transport, requests failing with it may or may
281 have been received by the server.
282 """
285class RequestTimedOut(TimeoutError):
286 """
287 Raised when request is timed out.
289 This error is currently not produced by aiocoap; it is deprecated. Users
290 can now catch error.TimeoutError, or newer more detailed subtypes
291 introduced later.
292 """
295class WaitingForClientTimedOut(TimeoutError):
296 """
297 Raised when server expects some client action:
299 - sending next PUT/POST request with block1 or block2 option
300 - sending next GET request with block2 option
302 but client does nothing.
304 This error is currently not produced by aiocoap; it is deprecated. Users
305 can now catch error.TimeoutError, or newer more detailed subtypes
306 introduced later.
307 """
310class ResourceChanged(Error):
311 """
312 The requested resource was modified during the request and could therefore
313 not be received in a consistent state.
314 """
317class UnexpectedBlock1Option(Error):
318 """
319 Raised when a server responds with block1 options that just don't match.
320 """
323class UnexpectedBlock2(Error):
324 """
325 Raised when a server responds with another block2 than expected.
326 """
329class MissingBlock2Option(Error):
330 """
331 Raised when response with Block2 option is expected
332 (previous response had Block2 option with More flag set),
333 but response without Block2 option is received.
334 """
337class NotObservable(Error):
338 """
339 The server did not accept the request to observe the resource.
340 """
343class ObservationCancelled(Error):
344 """
345 The server claimed that it will no longer sustain the observation.
346 """
349class UnparsableMessage(Error):
350 """
351 An incoming message does not look like CoAP.
353 Note that this happens rarely -- the requirements are just two bit at the
354 beginning of the message, and a minimum length.
355 """
358class LibraryShutdown(Error):
359 """The library or a transport registered with it was requested to shut
360 down; this error is raised in all outstanding requests."""
363class AnonymousHost(Error):
364 """This is raised when it is attempted to express as a reference a (base)
365 URI of a host or a resource that can not be reached by any process other
366 than this.
368 Typically, this happens when trying to serialize a link to a resource that
369 is hosted on a CoAP-over-TCP or -WebSockets client: Such resources can be
370 accessed for as long as the connection is active, but can not be used any
371 more once it is closed or even by another system."""
374__getattr__ = util.deprecation_getattr(
375 {
376 "UnsupportedMediaType": "UnsupportedContentFormat",
377 "RequestTimedOut": "TimeoutError",
378 "WaitingForClientTimedOut": "TimeoutError",
379 },
380 globals(),
381)