Coverage for aiocoap/numbers/codes.py: 92%

87 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-16 16:09 +0000

1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors 

2# 

3# SPDX-License-Identifier: MIT 

4 

5"""List of known values for the CoAP "Code" field. 

6 

7The values in this module correspond to the IANA registry "`CoRE Parameters`_", 

8subregistries "CoAP Method Codes" and "CoAP Response Codes". 

9 

10The codes come with methods that can be used to get their rough meaning, see 

11the :class:`Code` class for details. 

12 

13.. _`CoRE Parameters`: https://www.iana.org/assignments/core-parameters/core-parameters.xhtml 

14""" 

15 

16import warnings 

17 

18from ..util import ExtensibleIntEnum 

19 

20class Code(ExtensibleIntEnum): 

21 """Value for the CoAP "Code" field. 

22 

23 As the number range for the code values is separated, the rough meaning of 

24 a code can be determined using the :meth:`is_request`, :meth:`is_response` and 

25 :meth:`is_successful` methods.""" 

26 

27 EMPTY = 0 

28 GET = 1 

29 POST = 2 

30 PUT = 3 

31 DELETE = 4 

32 FETCH = 5 

33 PATCH = 6 

34 iPATCH = 7 

35 CREATED = 65 

36 DELETED = 66 

37 VALID = 67 

38 CHANGED = 68 

39 CONTENT = 69 

40 CONTINUE = 95 

41 BAD_REQUEST = 128 

42 UNAUTHORIZED = 129 

43 BAD_OPTION = 130 

44 FORBIDDEN = 131 

45 NOT_FOUND = 132 

46 METHOD_NOT_ALLOWED = 133 

47 NOT_ACCEPTABLE = 134 

48 REQUEST_ENTITY_INCOMPLETE = 136 

49 CONFLICT = (4 << 5) + 9 

50 PRECONDITION_FAILED = 140 

51 REQUEST_ENTITY_TOO_LARGE = 141 

52 UNSUPPORTED_CONTENT_FORMAT = 143 

53 @property 

54 def UNSUPPORTED_MEDIA_TYPE(self): 

55 warnings.warn("UNSUPPORTED_MEDIA_TYPE is a deprecated alias for UNSUPPORTED_CONTENT_FORMAT") 

56 return self.UNSUPPORTED_CONTENT_FORMAT 

57 UNPROCESSABLE_ENTITY = (4 << 5) + 22 

58 TOO_MANY_REQUESTS = (4 << 5) + 29 

59 INTERNAL_SERVER_ERROR = 160 

60 NOT_IMPLEMENTED = 161 

61 BAD_GATEWAY = 162 

62 SERVICE_UNAVAILABLE = 163 

63 GATEWAY_TIMEOUT = 164 

64 PROXYING_NOT_SUPPORTED = 165 

65 HOP_LIMIT_REACHED = (5 << 5) + 8 

66 

67 CSM = 225 

68 PING = 226 

69 PONG = 227 

70 RELEASE = 228 

71 ABORT = 229 

72 

73 def is_request(self): 

74 """True if the code is in the request code range""" 

75 return True if (self >= 1 and self < 32) else False 

76 

77 

78 def is_response(self): 

79 """True if the code is in the response code range""" 

80 return True if (self >= 64 and self < 192) else False 

81 

82 def is_signalling(self): 

83 return True if self >= 224 else False 

84 

85 

86 def is_successful(self): 

87 """True if the code is in the successful subrange of the response code range""" 

88 return True if (self >= 64 and self < 96) else False 

89 

90 def can_have_payload(self): 

91 """True if a message with that code can carry a payload. This is not 

92 checked for strictly, but used as an indicator.""" 

93 return self.is_response() or self in (self.POST, self.PUT, self.FETCH, self.PATCH, self.iPATCH) 

94 

95 @property 

96 def class_(self): 

97 """The class of a code (distinguishing whether it's successful, a 

98 request or a response error or more). 

99 

100 >>> Code.CONTENT 

101 <Successful Response Code 69 "2.05 Content"> 

102 >>> Code.CONTENT.class_ 

103 2 

104 >>> Code.BAD_GATEWAY 

105 <Response Code 162 "5.02 Bad Gateway"> 

106 >>> Code.BAD_GATEWAY.class_ 

107 5 

108 """ 

109 return self >> 5 

110 

111 @property 

112 def dotted(self): 

113 """The numeric value three-decimal-digits (c.dd) form""" 

114 return "%d.%02d" % divmod(self, 32) 

115 

116 @property 

117 def name_printable(self): 

118 """The name of the code in human-readable form""" 

119 return self.name.replace('_', ' ').title() 

120 

121 def __str__(self): 

122 """ 

123 >>> print(Code.GET) 

124 GET 

125 >>> print(Code.CONTENT) 

126 2.05 Content 

127 >>> print(Code.BAD_GATEWAY) 

128 5.02 Bad Gateway 

129 >>> print(Code(32)) 

130 32 

131 """ 

132 if self.is_request() or self is self.EMPTY: 

133 return self.name 

134 elif self.is_response() or self.is_signalling(): 

135 return "%s %s" % (self.dotted, self.name_printable) 

136 else: 

137 return "%d" % self 

138 

139 def _classification(self): 

140 return ("Successful " if self.is_successful() else "") + ("Request " if self.is_request() else "Response " if self.is_response() else "") 

141 

142 def __repr__(self): 

143 """ 

144 >>> Code.GET 

145 <Request Code 1 "GET"> 

146 >>> Code.CONTENT 

147 <Successful Response Code 69 "2.05 Content"> 

148 >>> Code.BAD_GATEWAY 

149 <Response Code 162 "5.02 Bad Gateway"> 

150 >>> Code(32) 

151 <Code 32 "32"> 

152 """ 

153 return '<%sCode %d "%s">' % (self._classification(), self, self) 

154 

155 name = property(lambda self: self._name if hasattr(self, "_name") else "(unknown)", lambda self, value: setattr(self, "_name", value), doc="The constant name of the code (equals name_printable readable in all-caps and with underscores)") 

156 

157 def _repr_html_(self): 

158 import html 

159 if hasattr(self, "_name"): 

160 return f'<abbr title="{self._classification()}Code {self.dotted}">{html.escape(self.name)}</abbr>' 

161 else: 

162 return f'<abbr title="Unknown {self._classification()}Code">{self.dotted}</abbr>' 

163 

164for k in vars(Code): 

165 if isinstance(getattr(Code, k), Code): 

166 locals()[k] = getattr(Code, k) 

167 

168__all__ = ['Code'] + [k for (k, v) in locals().items() if isinstance(v, Code)]