Coverage for aiocoap/numbers/codes.py: 97%
131 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
2#
3# SPDX-License-Identifier: MIT
5"""List of known values for the CoAP "Code" field.
7The values in this module correspond to the IANA registry "`CoRE Parameters`_",
8subregistries "CoAP Method Codes" and "CoAP Response Codes".
10The codes come with methods that can be used to get their rough meaning, see
11the :class:`Code` class for details.
13.. _`CoRE Parameters`: https://www.iana.org/assignments/core-parameters/core-parameters.xhtml
14"""
16import warnings
18from ..util import ExtensibleIntEnum
21class Code(ExtensibleIntEnum):
22 """Value for the CoAP "Code" field.
24 As the number range for the code values is separated, the rough meaning of
25 a code can be determined using the :meth:`is_request`, :meth:`is_response` and
26 :meth:`is_successful` methods."""
28 EMPTY = 0
29 GET = 1
30 POST = 2
31 PUT = 3
32 DELETE = 4
33 FETCH = 5
34 PATCH = 6
35 iPATCH = 7
36 CREATED = 65
37 DELETED = 66
38 VALID = 67
39 CHANGED = 68
40 CONTENT = 69
41 CONTINUE = 95
42 BAD_REQUEST = 128
43 UNAUTHORIZED = 129
44 BAD_OPTION = 130
45 FORBIDDEN = 131
46 NOT_FOUND = 132
47 METHOD_NOT_ALLOWED = 133
48 NOT_ACCEPTABLE = 134
49 REQUEST_ENTITY_INCOMPLETE = 136
50 CONFLICT = (4 << 5) + 9
51 PRECONDITION_FAILED = 140
52 REQUEST_ENTITY_TOO_LARGE = 141
53 UNSUPPORTED_CONTENT_FORMAT = 143
55 @property
56 def UNSUPPORTED_MEDIA_TYPE(self):
57 warnings.warn(
58 "UNSUPPORTED_MEDIA_TYPE is a deprecated alias for UNSUPPORTED_CONTENT_FORMAT"
59 )
60 return self.UNSUPPORTED_CONTENT_FORMAT
62 UNPROCESSABLE_ENTITY = (4 << 5) + 22
63 TOO_MANY_REQUESTS = (4 << 5) + 29
64 INTERNAL_SERVER_ERROR = 160
65 NOT_IMPLEMENTED = 161
66 BAD_GATEWAY = 162
67 SERVICE_UNAVAILABLE = 163
68 GATEWAY_TIMEOUT = 164
69 PROXYING_NOT_SUPPORTED = 165
70 HOP_LIMIT_REACHED = (5 << 5) + 8
72 CSM = 225
73 PING = 226
74 PONG = 227
75 RELEASE = 228
76 ABORT = 229
78 def is_request(self):
79 """True if the code is in the request code range"""
80 return True if (self >= 1 and self < 32) else False
82 def is_response(self):
83 """True if the code is in the response code range"""
84 return True if (self >= 64 and self < 192) else False
86 def is_signalling(self):
87 return True if self >= 224 else False
89 def is_successful(self):
90 """True if the code is in the successful subrange of the response code range"""
91 return True if (self >= 64 and self < 96) else False
93 def can_have_payload(self):
94 """True if a message with that code can carry a payload. This is not
95 checked for strictly, but used as an indicator."""
96 return self.is_response() or self in (
97 self.POST,
98 self.PUT,
99 self.FETCH,
100 self.PATCH,
101 self.iPATCH,
102 )
104 @property
105 def class_(self):
106 """The class of a code (distinguishing whether it's successful, a
107 request or a response error or more).
109 >>> Code.CONTENT
110 <Successful Response Code 69 "2.05 Content">
111 >>> Code.CONTENT.class_
112 2
113 >>> Code.BAD_GATEWAY
114 <Response Code 162 "5.02 Bad Gateway">
115 >>> Code.BAD_GATEWAY.class_
116 5
117 """
118 return self >> 5
120 @property
121 def dotted(self):
122 """The numeric value three-decimal-digits (c.dd) form"""
123 return "%d.%02d" % divmod(self, 32)
125 @property
126 def name_printable(self):
127 """The name of the code in human-readable form"""
128 return self.name.replace("_", " ").title()
130 def __str__(self):
131 """
132 >>> print(Code.GET)
133 GET
134 >>> print(Code.CONTENT)
135 2.05 Content
136 >>> print(Code.BAD_GATEWAY)
137 5.02 Bad Gateway
138 >>> print(Code(32))
139 32
140 """
141 if self.is_request() or self is self.EMPTY:
142 return self.name
143 elif self.is_response() or self.is_signalling():
144 return "%s %s" % (self.dotted, self.name_printable)
145 else:
146 return "%d" % self
148 def _classification(self):
149 return ("Successful " if self.is_successful() else "") + (
150 "Request "
151 if self.is_request()
152 else "Response "
153 if self.is_response()
154 else ""
155 )
157 def __repr__(self):
158 """
159 >>> Code.GET
160 <Request Code 1 "GET">
161 >>> Code.CONTENT
162 <Successful Response Code 69 "2.05 Content">
163 >>> Code.BAD_GATEWAY
164 <Response Code 162 "5.02 Bad Gateway">
165 >>> Code(32)
166 <Code 32 "32">
167 """
168 return '<%sCode %d "%s">' % (self._classification(), self, self)
170 def _repr_html_(self):
171 """
172 >>> Code.GET._repr_html_()
173 '<abbr title="Request Code 0.01">GET</abbr>'
174 >>> Code(31)._repr_html_()
175 '<abbr title="Unknown Request Code">0.31</abbr>'
176 """
177 import html
179 if self.name == "(unknown)":
180 return f'<abbr title="Unknown {self._classification()}Code">{self.dotted}</abbr>'
181 else:
182 return f'<abbr title="{self._classification()}Code {self.dotted}">{html.escape(self.name)}</abbr>'
184 @classmethod
185 def _missing_(cls, value):
186 constructed = super()._missing_(value)
187 constructed._name_ = "(unknown)"
188 return constructed
191# List is copied down to help mypy and other typing dependent tools to
192# understand the types.
193EMPTY = Code.EMPTY
194GET = Code.GET
195POST = Code.POST
196PUT = Code.PUT
197DELETE = Code.DELETE
198FETCH = Code.FETCH
199PATCH = Code.PATCH
200iPATCH = Code.iPATCH
201CREATED = Code.CREATED
202DELETED = Code.DELETED
203VALID = Code.VALID
204CHANGED = Code.CHANGED
205CONTENT = Code.CONTENT
206CONTINUE = Code.CONTINUE
207BAD_REQUEST = Code.BAD_REQUEST
208UNAUTHORIZED = Code.UNAUTHORIZED
209BAD_OPTION = Code.BAD_OPTION
210FORBIDDEN = Code.FORBIDDEN
211NOT_FOUND = Code.NOT_FOUND
212METHOD_NOT_ALLOWED = Code.METHOD_NOT_ALLOWED
213NOT_ACCEPTABLE = Code.NOT_ACCEPTABLE
214REQUEST_ENTITY_INCOMPLETE = Code.REQUEST_ENTITY_INCOMPLETE
215CONFLICT = Code.CONFLICT
216PRECONDITION_FAILED = Code.PRECONDITION_FAILED
217REQUEST_ENTITY_TOO_LARGE = Code.REQUEST_ENTITY_TOO_LARGE
218UNSUPPORTED_CONTENT_FORMAT = Code.UNSUPPORTED_CONTENT_FORMAT
219UNPROCESSABLE_ENTITY = Code.UNPROCESSABLE_ENTITY
220TOO_MANY_REQUESTS = Code.TOO_MANY_REQUESTS
221INTERNAL_SERVER_ERROR = Code.INTERNAL_SERVER_ERROR
222NOT_IMPLEMENTED = Code.NOT_IMPLEMENTED
223BAD_GATEWAY = Code.BAD_GATEWAY
224SERVICE_UNAVAILABLE = Code.SERVICE_UNAVAILABLE
225GATEWAY_TIMEOUT = Code.GATEWAY_TIMEOUT
226PROXYING_NOT_SUPPORTED = Code.PROXYING_NOT_SUPPORTED
227HOP_LIMIT_REACHED = Code.HOP_LIMIT_REACHED
228CSM = Code.CSM
229PING = Code.PING
230PONG = Code.PONG
231RELEASE = Code.RELEASE
232ABORT = Code.ABORT
233if __debug__:
234 for _code in Code:
235 if locals().get(_code.name) is not _code:
236 warnings.warn(
237 f"Locals list is out of sync; entry `{_code.name} = Code.{_code.name}` is missing"
238 )
241__all__ = ["Code"] + [
242 k for (k, v) in locals().items() if isinstance(v, Code) and not k.startswith("_")
243]