Coverage for aiocoap/numbers/optionnumbers.py: 94%
106 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-30 11:17 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-09-30 11:17 +0000
1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
2#
3# SPDX-License-Identifier: MIT
5"""Known values for CoAP option numbers
7The values defined in `OptionNumber` correspond to the IANA registry "CoRE
8Parameters", subregistries "CoAP Method Codes" and "CoAP Response Codes".
10The option numbers come with methods that can be used to evaluate their
11properties, see the `OptionNumber` class for details.
12"""
14import warnings
16from ..util import ExtensibleIntEnum
17from .. import optiontypes
20class OptionNumber(ExtensibleIntEnum):
21 """A CoAP option number.
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.
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."""
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 URI_PATH_ABBREV = 13 # from draft-ietf-core-uri-path-abbrev
42 MAX_AGE = 14
43 URI_QUERY = 15
44 HOP_LIMIT = 16
45 ACCEPT = 17
46 Q_BLOCK1 = 19
47 LOCATION_QUERY = 20
48 EDHOC = 21
49 BLOCK2 = 23
50 BLOCK1 = 27
51 SIZE2 = 28
52 Q_BLOCK2 = 31
53 PROXY_URI = 35
54 PROXY_SCHEME = 39
55 SIZE1 = 60
56 ECHO = 252
57 NO_RESPONSE = 258
58 REQUEST_TAG = 292
60 # experimental for draft-amsuess-core-cachable-oscore
61 #
62 # Using the number suggested there (rather than a high one) as this is
63 # going to be used in overhead comparisons.
64 REQUEST_HASH = 548
66 @property
67 def OBJECT_SECURITY(self):
68 warnings.warn("OBJECT_SECURITY is a deprecated alias for OSCORE")
69 return self.OSCORE
71 def __add__(self, delta):
72 """Addition makes sense on these due to the delta encoding in CoAP
73 serialization"""
74 return type(self)(int(self) + delta)
76 def is_critical(self):
77 return self & 0x01 == 0x01
79 def is_elective(self):
80 return not self.is_critical()
82 def is_unsafe(self):
83 return self & 0x02 == 0x02
85 def is_safetoforward(self):
86 return not self.is_unsafe()
88 def is_nocachekey(self):
89 if self.is_unsafe():
90 raise ValueError("NoCacheKey is only meaningful for safe options")
91 return self & 0x1E == 0x1C
93 def is_cachekey(self):
94 return not self.is_nocachekey()
96 def _get_format(self):
97 if hasattr(self, "_format"):
98 return self._format
99 else:
100 return optiontypes.OpaqueOption
102 def set_format(self, value):
103 """Set the serialization format.
105 This affects any use of the option throughout the program; existing
106 options should not be altered incompatibly. Use this on custom or
107 experimental options.
109 This is available as a setter function in addition to write access
110 through the `format` property to satisfy requirements imposed by mypy's
111 special handling of enums.
112 """
113 if hasattr(self, "_format"):
114 if self._format != value:
115 warnings.warn(
116 "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."
117 )
118 self._format = value
120 format = property(
121 _get_format,
122 set_format,
123 doc="Serialization format; see :func:`~aiocoap.numbers.optionnumbers.OptionNumber.set_format`",
124 )
126 def create_option(self, decode=None, value=None):
127 """Return an Option element of the appropriate class from this option
128 number.
130 An initial value may be set using the decode or value options, and will
131 be fed to the resulting object's decode method or value property,
132 respectively."""
133 option = self.format(self)
134 if decode is not None:
135 option.decode(decode)
136 if value is not None:
137 option.value = value
138 return option
140 @property
141 def name_printable(self):
142 """The name of the code in human-readable form
144 This is only available on options that have a known name."""
145 return self.name.replace("_", " ").title().replace(" ", "-")
147 def _repr_html_(self):
148 import html
150 properties = f"{'critical' if self.is_critical() else 'elective'}, {'safe-to-forward' if self.is_safetoforward() else 'proxy unsafe'}"
151 if self.is_safetoforward():
152 properties += (
153 ", part of the cache key"
154 if self.is_cachekey()
155 else ", not part of the cache key"
156 )
157 if hasattr(self, "name"):
158 return f'<abbr title="option {int(self)}: {properties}">{html.escape(self.name_printable)}</abbr>'
159 else:
160 return f'<abbr title="{properties}">Option {int(self)}</abbr>'
163# OpaqueOption is set on formats where it is known to be used even though it is
164# the default. This allows developers to rely on those interfaces to be stable
165# (or at least to be notified visibly in the release notes).
167# RFC 7252
169OptionNumber.IF_MATCH.set_format(optiontypes.OpaqueOption)
170OptionNumber.URI_HOST.set_format(optiontypes.StringOption)
171OptionNumber.ETAG.set_format(optiontypes.OpaqueOption)
172OptionNumber.URI_PORT.set_format(optiontypes.UintOption)
173OptionNumber.LOCATION_PATH.set_format(optiontypes.StringOption)
174OptionNumber.URI_PATH.set_format(optiontypes.StringOption)
175OptionNumber.CONTENT_FORMAT.set_format(optiontypes.ContentFormatOption)
176OptionNumber.MAX_AGE.set_format(optiontypes.UintOption)
177OptionNumber.URI_QUERY.set_format(optiontypes.StringOption)
178OptionNumber.ACCEPT.set_format(optiontypes.ContentFormatOption)
179OptionNumber.LOCATION_QUERY.set_format(optiontypes.StringOption)
180OptionNumber.PROXY_URI.set_format(optiontypes.StringOption)
181OptionNumber.PROXY_SCHEME.set_format(optiontypes.StringOption)
182OptionNumber.SIZE1.set_format(optiontypes.UintOption)
184# RFC 7959
186OptionNumber.BLOCK2.set_format(optiontypes.BlockOption)
187OptionNumber.BLOCK1.set_format(optiontypes.BlockOption)
188OptionNumber.SIZE2.set_format(optiontypes.UintOption)
190# RFC 7641
192OptionNumber.OBSERVE.set_format(optiontypes.UintOption)
194# RFC 7967
196OptionNumber.NO_RESPONSE.set_format(optiontypes.UintOption)
198# RFC 8613
200OptionNumber.OSCORE.set_format(optiontypes.OpaqueOption)
202# RFC 9175
204OptionNumber.ECHO.set_format(optiontypes.OpaqueOption)
205OptionNumber.REQUEST_TAG.set_format(optiontypes.OpaqueOption)
207# RFC 8768
209OptionNumber.HOP_LIMIT.set_format(optiontypes.UintOption)
211# experimental for draft-amsuess-core-cachable-oscore
213OptionNumber.REQUEST_HASH.set_format(optiontypes.OpaqueOption)
215# draft-ietf-core-uri-path-abbrev
217# FIXME: Should we express this as an enum right away?
218OptionNumber.URI_PATH_ABBREV.set_format(optiontypes.UintOption)