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
« 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
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 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
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
65 @property
66 def OBJECT_SECURITY(self):
67 warnings.warn("OBJECT_SECURITY is a deprecated alias for OSCORE")
68 return self.OSCORE
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)
75 def is_critical(self):
76 return self & 0x01 == 0x01
78 def is_elective(self):
79 return not self.is_critical()
81 def is_unsafe(self):
82 return self & 0x02 == 0x02
84 def is_safetoforward(self):
85 return not self.is_unsafe()
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
92 def is_cachekey(self):
93 return not self.is_nocachekey()
95 def _get_format(self):
96 if hasattr(self, "_format"):
97 return self._format
98 else:
99 return optiontypes.OpaqueOption
101 def set_format(self, value):
102 """Set the serialization format.
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.
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
119 format = property(
120 _get_format,
121 set_format,
122 doc="Serialization format; see :func:`~aiocoap.numbers.optionnumbers.OptionNumber.set_format`",
123 )
125 def create_option(self, decode=None, value=None):
126 """Return an Option element of the appropriate class from this option
127 number.
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
139 @property
140 def name_printable(self):
141 """The name of the code in human-readable form
143 This is only available on options that have a known name."""
144 return self.name.replace("_", " ").title().replace(" ", "-")
146 def _repr_html_(self):
147 import html
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>'
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).
166# RFC 7252
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)
183# RFC 7959
185OptionNumber.BLOCK2.set_format(optiontypes.BlockOption)
186OptionNumber.BLOCK1.set_format(optiontypes.BlockOption)
187OptionNumber.SIZE2.set_format(optiontypes.UintOption)
189# RFC 7641
191OptionNumber.OBSERVE.set_format(optiontypes.UintOption)
193# RFC 7967
195OptionNumber.NO_RESPONSE.set_format(optiontypes.UintOption)
197# RFC 8613
199OptionNumber.OSCORE.set_format(optiontypes.OpaqueOption)
201# RFC 9175
203OptionNumber.ECHO.set_format(optiontypes.OpaqueOption)
204OptionNumber.REQUEST_TAG.set_format(optiontypes.OpaqueOption)
206# RFC 8768
208OptionNumber.HOP_LIMIT.set_format(optiontypes.UintOption)
210# experimental for draft-amsuess-core-cachable-oscore
212OptionNumber.REQUEST_HASH.set_format(optiontypes.OpaqueOption)