Coverage for src/aiocoap/numbers/optionnumbers.py: 0%

104 statements  

« prev     ^ index     » next       coverage.py v7.7.0, created at 2025-03-20 17:26 +0000

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

2# 

3# SPDX-License-Identifier: MIT 

4 

5"""Known values for CoAP option numbers 

6 

7The values defined in `OptionNumber` correspond to the IANA registry "CoRE 

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

9 

10The option numbers come with methods that can be used to evaluate their 

11properties, see the `OptionNumber` class for details. 

12""" 

13 

14import warnings 

15 

16from ..util import ExtensibleIntEnum 

17from .. import optiontypes 

18 

19 

20class OptionNumber(ExtensibleIntEnum): 

21 """A CoAP option number. 

22 

23 As the option number contains information on whether the option is 

24 critical, and whether it is safe-to-forward, those properties can be 

25 queried using the `is_*` group of methods. 

26 

27 Note that whether an option may be repeated or not does not only depend on 

28 the option, but also on the context, and is thus handled in the `Options` 

29 object instead.""" 

30 

31 IF_MATCH = 1 

32 URI_HOST = 3 

33 ETAG = 4 

34 IF_NONE_MATCH = 5 

35 OBSERVE = 6 

36 URI_PORT = 7 

37 LOCATION_PATH = 8 

38 OSCORE = 9 

39 URI_PATH = 11 

40 CONTENT_FORMAT = 12 

41 MAX_AGE = 14 

42 URI_QUERY = 15 

43 HOP_LIMIT = 16 

44 ACCEPT = 17 

45 Q_BLOCK1 = 19 

46 LOCATION_QUERY = 20 

47 EDHOC = 21 

48 BLOCK2 = 23 

49 BLOCK1 = 27 

50 SIZE2 = 28 

51 Q_BLOCK2 = 31 

52 PROXY_URI = 35 

53 PROXY_SCHEME = 39 

54 SIZE1 = 60 

55 ECHO = 252 

56 NO_RESPONSE = 258 

57 REQUEST_TAG = 292 

58 

59 # experimental for draft-amsuess-core-cachable-oscore 

60 # 

61 # Using the number suggested there (rather than a high one) as this is 

62 # going to be used in overhead comparisons. 

63 REQUEST_HASH = 548 

64 

65 @property 

66 def OBJECT_SECURITY(self): 

67 warnings.warn("OBJECT_SECURITY is a deprecated alias for OSCORE") 

68 return self.OSCORE 

69 

70 def __add__(self, delta): 

71 """Addition makes sense on these due to the delta encoding in CoAP 

72 serialization""" 

73 return type(self)(int(self) + delta) 

74 

75 def is_critical(self): 

76 return self & 0x01 == 0x01 

77 

78 def is_elective(self): 

79 return not self.is_critical() 

80 

81 def is_unsafe(self): 

82 return self & 0x02 == 0x02 

83 

84 def is_safetoforward(self): 

85 return not self.is_unsafe() 

86 

87 def is_nocachekey(self): 

88 if self.is_unsafe(): 

89 raise ValueError("NoCacheKey is only meaningful for safe options") 

90 return self & 0x1E == 0x1C 

91 

92 def is_cachekey(self): 

93 return not self.is_nocachekey() 

94 

95 def _get_format(self): 

96 if hasattr(self, "_format"): 

97 return self._format 

98 else: 

99 return optiontypes.OpaqueOption 

100 

101 def set_format(self, value): 

102 """Set the serialization format. 

103 

104 This affects any use of the option throughout the program; existing 

105 options should not be altered incompatibly. Use this on custom or 

106 experimental options. 

107 

108 This is available as a setter function in addition to write access 

109 through the `format` property to satisfy requirements imposed by mypy's 

110 special handling of enums. 

111 """ 

112 if hasattr(self, "_format"): 

113 if self._format != value: 

114 warnings.warn( 

115 "Altering the serialization format of {self}. This is a severe interoperability hazard with other modules, and should only be used during experimentation. This warning may be converted into an error at any time." 

116 ) 

117 self._format = value 

118 

119 format = property( 

120 _get_format, 

121 set_format, 

122 doc="Serialization format; see :func:`~aiocoap.numbers.optionnumbers.OptionNumber.set_format`", 

123 ) 

124 

125 def create_option(self, decode=None, value=None): 

126 """Return an Option element of the appropriate class from this option 

127 number. 

128 

129 An initial value may be set using the decode or value options, and will 

130 be fed to the resulting object's decode method or value property, 

131 respectively.""" 

132 option = self.format(self) 

133 if decode is not None: 

134 option.decode(decode) 

135 if value is not None: 

136 option.value = value 

137 return option 

138 

139 @property 

140 def name_printable(self): 

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

142 

143 This is only available on options that have a known name.""" 

144 return self.name.replace("_", " ").title().replace(" ", "-") 

145 

146 def _repr_html_(self): 

147 import html 

148 

149 properties = f"{'critical' if self.is_critical() else 'elective'}, {'safe-to-forward' if self.is_safetoforward() else 'proxy unsafe'}" 

150 if self.is_safetoforward(): 

151 properties += ( 

152 ", part of the cache key" 

153 if self.is_cachekey() 

154 else ", not part of the cache key" 

155 ) 

156 if hasattr(self, "name"): 

157 return f'<abbr title="option {int(self)}: {properties}">{html.escape(self.name_printable)}</abbr>' 

158 else: 

159 return f'<abbr title="{properties}">Option {int(self)}</abbr>' 

160 

161 

162# OpaqueOption is set on formats where it is known to be used even though it is 

163# the default. This allows developers to rely on those interfaces to be stable 

164# (or at least to be notified visibly in the release notes). 

165 

166# RFC 7252 

167 

168OptionNumber.IF_MATCH.set_format(optiontypes.OpaqueOption) 

169OptionNumber.URI_HOST.set_format(optiontypes.StringOption) 

170OptionNumber.ETAG.set_format(optiontypes.OpaqueOption) 

171OptionNumber.URI_PORT.set_format(optiontypes.UintOption) 

172OptionNumber.LOCATION_PATH.set_format(optiontypes.StringOption) 

173OptionNumber.URI_PATH.set_format(optiontypes.StringOption) 

174OptionNumber.CONTENT_FORMAT.set_format(optiontypes.ContentFormatOption) 

175OptionNumber.MAX_AGE.set_format(optiontypes.UintOption) 

176OptionNumber.URI_QUERY.set_format(optiontypes.StringOption) 

177OptionNumber.ACCEPT.set_format(optiontypes.ContentFormatOption) 

178OptionNumber.LOCATION_QUERY.set_format(optiontypes.StringOption) 

179OptionNumber.PROXY_URI.set_format(optiontypes.StringOption) 

180OptionNumber.PROXY_SCHEME.set_format(optiontypes.StringOption) 

181OptionNumber.SIZE1.set_format(optiontypes.UintOption) 

182 

183# RFC 7959 

184 

185OptionNumber.BLOCK2.set_format(optiontypes.BlockOption) 

186OptionNumber.BLOCK1.set_format(optiontypes.BlockOption) 

187OptionNumber.SIZE2.set_format(optiontypes.UintOption) 

188 

189# RFC 7641 

190 

191OptionNumber.OBSERVE.set_format(optiontypes.UintOption) 

192 

193# RFC 7967 

194 

195OptionNumber.NO_RESPONSE.set_format(optiontypes.UintOption) 

196 

197# RFC 8613 

198 

199OptionNumber.OSCORE.set_format(optiontypes.OpaqueOption) 

200 

201# RFC 9175 

202 

203OptionNumber.ECHO.set_format(optiontypes.OpaqueOption) 

204OptionNumber.REQUEST_TAG.set_format(optiontypes.OpaqueOption) 

205 

206# RFC 8768 

207 

208OptionNumber.HOP_LIMIT.set_format(optiontypes.UintOption) 

209 

210# experimental for draft-amsuess-core-cachable-oscore 

211 

212OptionNumber.REQUEST_HASH.set_format(optiontypes.OpaqueOption)