odxtools 9.6.1__py3-none-any.whl → 10.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. odxtools/additionalaudience.py +3 -3
  2. odxtools/addressing.py +8 -0
  3. odxtools/admindata.py +8 -8
  4. odxtools/audience.py +10 -10
  5. odxtools/basecomparam.py +7 -20
  6. odxtools/basevariantpattern.py +4 -5
  7. odxtools/basicstructure.py +12 -11
  8. odxtools/cli/_print_utils.py +35 -23
  9. odxtools/cli/browse.py +9 -9
  10. odxtools/cli/compare.py +24 -24
  11. odxtools/cli/decode.py +3 -4
  12. odxtools/cli/find.py +4 -5
  13. odxtools/cli/list.py +7 -7
  14. odxtools/cli/main.py +2 -2
  15. odxtools/cli/snoop.py +3 -3
  16. odxtools/codec.py +3 -186
  17. odxtools/commrelation.py +12 -19
  18. odxtools/commrelationvaluetype.py +9 -0
  19. odxtools/companydata.py +5 -5
  20. odxtools/companydocinfo.py +8 -8
  21. odxtools/companyrevisioninfo.py +5 -5
  22. odxtools/companyspecificinfo.py +5 -5
  23. odxtools/comparam.py +3 -3
  24. odxtools/comparaminstance.py +10 -10
  25. odxtools/comparamspec.py +3 -3
  26. odxtools/comparamsubset.py +5 -5
  27. odxtools/complexcomparam.py +7 -7
  28. odxtools/compositecodec.py +191 -0
  29. odxtools/compumethods/compucategory.py +13 -0
  30. odxtools/compumethods/compucodecompumethod.py +6 -5
  31. odxtools/compumethods/compuconst.py +4 -5
  32. odxtools/compumethods/compudefaultvalue.py +1 -2
  33. odxtools/compumethods/compuinternaltophys.py +6 -6
  34. odxtools/compumethods/compumethod.py +6 -17
  35. odxtools/compumethods/compuphystointernal.py +6 -6
  36. odxtools/compumethods/compurationalcoeffs.py +4 -4
  37. odxtools/compumethods/compuscale.py +9 -10
  38. odxtools/compumethods/createanycompumethod.py +1 -2
  39. odxtools/compumethods/identicalcompumethod.py +1 -2
  40. odxtools/compumethods/intervaltype.py +8 -0
  41. odxtools/compumethods/limit.py +13 -19
  42. odxtools/compumethods/linearcompumethod.py +4 -3
  43. odxtools/compumethods/linearsegment.py +14 -15
  44. odxtools/compumethods/ratfunccompumethod.py +5 -4
  45. odxtools/compumethods/ratfuncsegment.py +7 -8
  46. odxtools/compumethods/scalelinearcompumethod.py +10 -9
  47. odxtools/compumethods/scaleratfunccompumethod.py +6 -5
  48. odxtools/compumethods/tabintpcompumethod.py +19 -20
  49. odxtools/compumethods/texttablecompumethod.py +5 -4
  50. odxtools/createanycomparam.py +2 -4
  51. odxtools/createanydiagcodedtype.py +1 -2
  52. odxtools/database.py +9 -8
  53. odxtools/dataobjectproperty.py +10 -10
  54. odxtools/decodestate.py +5 -5
  55. odxtools/description.py +6 -22
  56. odxtools/determinenumberofitems.py +4 -4
  57. odxtools/diagclasstype.py +11 -0
  58. odxtools/diagcodedtype.py +7 -7
  59. odxtools/diagcomm.py +19 -42
  60. odxtools/diagdatadictionaryspec.py +6 -6
  61. odxtools/diaglayercontainer.py +4 -4
  62. odxtools/diaglayers/basevariant.py +10 -9
  63. odxtools/diaglayers/basevariantraw.py +9 -9
  64. odxtools/diaglayers/diaglayer.py +20 -19
  65. odxtools/diaglayers/diaglayerraw.py +10 -10
  66. odxtools/diaglayers/diaglayertype.py +1 -2
  67. odxtools/diaglayers/ecushareddata.py +4 -4
  68. odxtools/diaglayers/ecushareddataraw.py +6 -6
  69. odxtools/diaglayers/ecuvariant.py +11 -10
  70. odxtools/diaglayers/ecuvariantraw.py +9 -9
  71. odxtools/diaglayers/functionalgroup.py +8 -7
  72. odxtools/diaglayers/functionalgroupraw.py +7 -7
  73. odxtools/diaglayers/hierarchyelement.py +43 -49
  74. odxtools/diaglayers/hierarchyelementraw.py +4 -4
  75. odxtools/diaglayers/protocol.py +4 -4
  76. odxtools/diaglayers/protocolraw.py +6 -6
  77. odxtools/diagnostictroublecode.py +8 -8
  78. odxtools/diagservice.py +21 -97
  79. odxtools/diagvariable.py +14 -14
  80. odxtools/docrevision.py +11 -11
  81. odxtools/dopbase.py +6 -6
  82. odxtools/dtcconnector.py +45 -0
  83. odxtools/dtcdop.py +15 -56
  84. odxtools/dynamicendmarkerfield.py +5 -4
  85. odxtools/dynamiclengthfield.py +5 -4
  86. odxtools/dyndefinedspec.py +7 -159
  87. odxtools/dynenddopref.py +5 -5
  88. odxtools/dyniddefmodeinfo.py +161 -0
  89. odxtools/ecuvariantpattern.py +4 -5
  90. odxtools/element.py +5 -6
  91. odxtools/encodestate.py +11 -11
  92. odxtools/encoding.py +2 -3
  93. odxtools/endofpdufield.py +6 -6
  94. odxtools/envdataconnector.py +49 -0
  95. odxtools/environmentdata.py +3 -4
  96. odxtools/environmentdatadescription.py +11 -11
  97. odxtools/exceptions.py +5 -5
  98. odxtools/externalaccessmethod.py +22 -0
  99. odxtools/externaldoc.py +23 -0
  100. odxtools/field.py +9 -10
  101. odxtools/functionalclass.py +4 -4
  102. odxtools/inputparam.py +6 -6
  103. odxtools/internalconstr.py +4 -5
  104. odxtools/isotp_state_machine.py +12 -11
  105. odxtools/leadinglengthinfotype.py +2 -3
  106. odxtools/library.py +5 -5
  107. odxtools/linkeddtcdop.py +62 -0
  108. odxtools/loadfile.py +5 -6
  109. odxtools/matchingbasevariantparameter.py +2 -3
  110. odxtools/matchingparameter.py +7 -7
  111. odxtools/minmaxlengthtype.py +5 -11
  112. odxtools/modification.py +4 -4
  113. odxtools/multiplexer.py +11 -11
  114. odxtools/multiplexercase.py +6 -6
  115. odxtools/multiplexerdefaultcase.py +6 -6
  116. odxtools/multiplexerswitchkey.py +4 -4
  117. odxtools/nameditemlist.py +14 -14
  118. odxtools/negoutputparam.py +3 -3
  119. odxtools/obd.py +1 -2
  120. odxtools/odxcategory.py +6 -6
  121. odxtools/odxlink.py +19 -20
  122. odxtools/odxtypes.py +21 -18
  123. odxtools/outputparam.py +4 -4
  124. odxtools/parameterinfo.py +2 -2
  125. odxtools/parameters/codedconstparameter.py +5 -5
  126. odxtools/parameters/createanyparameter.py +1 -2
  127. odxtools/parameters/dynamicparameter.py +2 -3
  128. odxtools/parameters/lengthkeyparameter.py +5 -5
  129. odxtools/parameters/matchingrequestparameter.py +3 -4
  130. odxtools/parameters/nrcconstparameter.py +7 -7
  131. odxtools/parameters/parameter.py +11 -11
  132. odxtools/parameters/parameterwithdop.py +9 -9
  133. odxtools/parameters/physicalconstantparameter.py +4 -4
  134. odxtools/parameters/reservedparameter.py +3 -4
  135. odxtools/parameters/rowfragment.py +7 -0
  136. odxtools/parameters/systemparameter.py +2 -3
  137. odxtools/parameters/tableentryparameter.py +4 -9
  138. odxtools/parameters/tablekeyparameter.py +10 -10
  139. odxtools/parameters/tablestructparameter.py +7 -7
  140. odxtools/parameters/valueparameter.py +7 -7
  141. odxtools/paramlengthinfotype.py +5 -3
  142. odxtools/parentref.py +9 -9
  143. odxtools/physicaldimension.py +11 -11
  144. odxtools/physicaltype.py +4 -12
  145. odxtools/posresponsesuppressible.py +72 -0
  146. odxtools/preconditionstateref.py +7 -7
  147. odxtools/progcode.py +6 -6
  148. odxtools/protstack.py +4 -4
  149. odxtools/radix.py +9 -0
  150. odxtools/relateddiagcommref.py +22 -0
  151. odxtools/relateddoc.py +6 -6
  152. odxtools/request.py +14 -12
  153. odxtools/response.py +15 -13
  154. odxtools/scaleconstr.py +4 -12
  155. odxtools/servicebinner.py +5 -5
  156. odxtools/singleecujob.py +4 -4
  157. odxtools/snrefcontext.py +2 -2
  158. odxtools/specialdata.py +5 -5
  159. odxtools/specialdatagroup.py +9 -9
  160. odxtools/specialdatagroupcaption.py +3 -3
  161. odxtools/standardizationlevel.py +9 -0
  162. odxtools/standardlengthtype.py +12 -21
  163. odxtools/state.py +3 -3
  164. odxtools/statechart.py +4 -4
  165. odxtools/statemachine.py +4 -3
  166. odxtools/statetransition.py +5 -18
  167. odxtools/statetransitionref.py +18 -18
  168. odxtools/staticfield.py +5 -4
  169. odxtools/structure.py +2 -3
  170. odxtools/subcomponent.py +12 -245
  171. odxtools/subcomponentparamconnector.py +103 -0
  172. odxtools/subcomponentpattern.py +42 -0
  173. odxtools/swvariable.py +3 -4
  174. odxtools/table.py +17 -55
  175. odxtools/tablediagcommconnector.py +47 -0
  176. odxtools/tablerow.py +30 -30
  177. odxtools/tablerowconnector.py +46 -0
  178. odxtools/teammember.py +11 -11
  179. odxtools/templates/macros/printService.xml.jinja2 +2 -1
  180. odxtools/termination.py +8 -0
  181. odxtools/text.py +2 -3
  182. odxtools/transmode.py +9 -0
  183. odxtools/uds.py +2 -3
  184. odxtools/unit.py +9 -9
  185. odxtools/unitgroup.py +6 -11
  186. odxtools/unitgroupcategory.py +7 -0
  187. odxtools/unitspec.py +6 -6
  188. odxtools/usage.py +9 -0
  189. odxtools/utils.py +31 -2
  190. odxtools/validtype.py +9 -0
  191. odxtools/variablegroup.py +2 -2
  192. odxtools/variantmatcher.py +10 -10
  193. odxtools/variantpattern.py +3 -3
  194. odxtools/version.py +2 -2
  195. odxtools/writepdxfile.py +5 -5
  196. odxtools/xdoc.py +9 -9
  197. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/METADATA +4 -5
  198. odxtools-10.0.0.dist-info/RECORD +264 -0
  199. odxtools-9.6.1.dist-info/RECORD +0 -238
  200. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/WHEEL +0 -0
  201. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/entry_points.txt +0 -0
  202. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/licenses/LICENSE +0 -0
  203. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,161 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+ from xml.etree import ElementTree
5
+
6
+ from .diagclasstype import DiagClassType
7
+ from .diagcomm import DiagComm
8
+ from .exceptions import odxassert, odxraise, odxrequire
9
+ from .nameditemlist import NamedItemList
10
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
11
+ from .snrefcontext import SnRefContext
12
+ from .table import Table
13
+
14
+
15
+ @dataclass
16
+ class DynIdDefModeInfo:
17
+ def_mode: str
18
+
19
+ clear_dyn_def_message_ref: OdxLinkRef | None
20
+ clear_dyn_def_message_snref: str | None
21
+
22
+ read_dyn_def_message_ref: OdxLinkRef | None
23
+ read_dyn_def_message_snref: str | None
24
+
25
+ dyn_def_message_ref: OdxLinkRef | None
26
+ dyn_def_message_snref: str | None
27
+
28
+ supported_dyn_ids: list[bytes]
29
+ selection_table_refs: list[OdxLinkRef | str]
30
+
31
+ @property
32
+ def clear_dyn_def_message(self) -> DiagComm:
33
+ return self._clear_dyn_def_message
34
+
35
+ @property
36
+ def read_dyn_def_message(self) -> DiagComm:
37
+ return self._read_dyn_def_message
38
+
39
+ @property
40
+ def dyn_def_message(self) -> DiagComm:
41
+ return self._dyn_def_message
42
+
43
+ @property
44
+ def selection_tables(self) -> NamedItemList[Table]:
45
+ return self._selection_tables
46
+
47
+ @staticmethod
48
+ def from_et(et_element: ElementTree.Element,
49
+ doc_frags: list[OdxDocFragment]) -> "DynIdDefModeInfo":
50
+ def_mode = odxrequire(et_element.findtext("DEF-MODE"))
51
+
52
+ clear_dyn_def_message_ref = OdxLinkRef.from_et(
53
+ et_element.find("CLEAR-DYN-DEF-MESSAGE-REF"), doc_frags)
54
+ clear_dyn_def_message_snref = None
55
+ if (snref_elem := et_element.find("CLEAR-DYN-DEF-MESSAGE-SNREF")) is not None:
56
+ clear_dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
57
+
58
+ read_dyn_def_message_ref = OdxLinkRef.from_et(
59
+ et_element.find("READ-DYN-DEF-MESSAGE-REF"), doc_frags)
60
+ read_dyn_def_message_snref = None
61
+ if (snref_elem := et_element.find("READ-DYN-DEF-MESSAGE-SNREF")) is not None:
62
+ read_dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
63
+
64
+ dyn_def_message_ref = OdxLinkRef.from_et(et_element.find("DYN-DEF-MESSAGE-REF"), doc_frags)
65
+ dyn_def_message_snref = None
66
+ if (snref_elem := et_element.find("DYN-DEF-MESSAGE-SNREF")) is not None:
67
+ dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
68
+
69
+ supported_dyn_ids = [
70
+ bytes.fromhex(odxrequire(x.text))
71
+ for x in et_element.iterfind("SUPPORTED-DYN-IDS/SUPPORTED-DYN-ID")
72
+ ]
73
+
74
+ selection_table_refs: list[OdxLinkRef | str] = []
75
+ if (st_elems := et_element.find("SELECTION-TABLE-REFS")) is not None:
76
+ for st_elem in st_elems:
77
+ if st_elem.tag == "SELECTION-TABLE-REF":
78
+ selection_table_refs.append(OdxLinkRef.from_et(st_elem, doc_frags))
79
+ elif st_elem.tag == "SELECTION-TABLE-SNREF":
80
+ selection_table_refs.append(odxrequire(st_elem.get("SHORT-NAME")))
81
+ else:
82
+ odxraise()
83
+
84
+ return DynIdDefModeInfo(
85
+ def_mode=def_mode,
86
+ clear_dyn_def_message_ref=clear_dyn_def_message_ref,
87
+ clear_dyn_def_message_snref=clear_dyn_def_message_snref,
88
+ read_dyn_def_message_ref=read_dyn_def_message_ref,
89
+ read_dyn_def_message_snref=read_dyn_def_message_snref,
90
+ dyn_def_message_ref=dyn_def_message_ref,
91
+ dyn_def_message_snref=dyn_def_message_snref,
92
+ supported_dyn_ids=supported_dyn_ids,
93
+ selection_table_refs=selection_table_refs,
94
+ )
95
+
96
+ def __post_init__(self) -> None:
97
+ odxassert(
98
+ self.clear_dyn_def_message_ref is not None or
99
+ self.clear_dyn_def_message_snref is not None,
100
+ "A CLEAR-DYN-DEF-MESSAGE must be specified")
101
+ odxassert(
102
+ self.read_dyn_def_message_ref is not None or
103
+ self.read_dyn_def_message_snref is not None, "A READ-DYN-DEF-MESSAGE must be specified")
104
+ odxassert(self.dyn_def_message_ref is not None or self.dyn_def_message_snref is not None,
105
+ "A DYN-DEF-MESSAGE must be specified")
106
+
107
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
108
+ result: dict[OdxLinkId, Any] = {}
109
+
110
+ return result
111
+
112
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
113
+ if self.clear_dyn_def_message_ref is not None:
114
+ self._clear_dyn_def_message = odxlinks.resolve(self.clear_dyn_def_message_ref, DiagComm)
115
+
116
+ if self.read_dyn_def_message_ref is not None:
117
+ self._read_dyn_def_message = odxlinks.resolve(self.read_dyn_def_message_ref, DiagComm)
118
+
119
+ if self.dyn_def_message_ref is not None:
120
+ self._dyn_def_message = odxlinks.resolve(self.dyn_def_message_ref, DiagComm)
121
+
122
+ # resolve the selection tables that are referenced via ODXLINK
123
+ self._selection_tables = NamedItemList[Table]()
124
+ for x in self.selection_table_refs:
125
+ if isinstance(x, OdxLinkRef):
126
+ self._selection_tables.append(odxlinks.resolve(x, Table))
127
+
128
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
129
+ diag_layer = odxrequire(context.diag_layer)
130
+
131
+ if self.clear_dyn_def_message_snref is not None:
132
+ self._clear_dyn_def_message = resolve_snref(self.clear_dyn_def_message_snref,
133
+ diag_layer.diag_comms, DiagComm)
134
+
135
+ if self.read_dyn_def_message_snref is not None:
136
+ self._read_dyn_def_message = resolve_snref(self.read_dyn_def_message_snref,
137
+ diag_layer.diag_comms, DiagComm)
138
+
139
+ if self.dyn_def_message_snref is not None:
140
+ self._dyn_def_message = resolve_snref(self.dyn_def_message_snref, diag_layer.diag_comms,
141
+ DiagComm)
142
+
143
+ if self._clear_dyn_def_message.diagnostic_class != DiagClassType.CLEAR_DYN_DEF_MESSAGE:
144
+ odxraise(
145
+ f"Diagnostic communication object of wrong type referenced: "
146
+ f"({odxrequire(self._clear_dyn_def_message.diagnostic_class).value} instead of "
147
+ f"CLEAR-DYN-DEF-MESSAGE)")
148
+ if self._read_dyn_def_message.diagnostic_class != DiagClassType.READ_DYN_DEFINED_MESSAGE:
149
+ odxraise(f"Diagnostic communication object of wrong type referenced: "
150
+ f"({odxrequire(self._read_dyn_def_message.diagnostic_class).value} instead of "
151
+ f"READ-DYN-DEFINED-MESSAGE)")
152
+ if self._dyn_def_message.diagnostic_class != DiagClassType.DYN_DEF_MESSAGE:
153
+ odxraise(f"Diagnostic communication object of wrong type referenced: "
154
+ f"({odxrequire(self._dyn_def_message.diagnostic_class).value} instead of "
155
+ f"DYN-DEF-MESSAGE)")
156
+
157
+ # resolve the remaining selection tables that are referenced via SNREF
158
+ ddd_spec = odxrequire(diag_layer.diag_data_dictionary_spec)
159
+ for i, x in enumerate(self.selection_table_refs):
160
+ if isinstance(x, str):
161
+ self._selection_tables.insert(i, resolve_snref(x, ddd_spec.tables, Table))
@@ -1,6 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List, Union
4
3
  from xml.etree import ElementTree
5
4
 
6
5
  from typing_extensions import override
@@ -17,16 +16,16 @@ class EcuVariantPattern(VariantPattern):
17
16
  """ECU variant patterns are variant patterns used to identify the
18
17
  concrete variant of an ECU.
19
18
  """
20
- matching_parameters: List[MatchingParameter]
19
+ matching_parameters: list[MatchingParameter]
21
20
 
22
21
  @override
23
- def get_matching_parameters(
24
- self) -> Union[List[MatchingParameter], List[MatchingBaseVariantParameter]]:
22
+ def get_matching_parameters(self
23
+ ) -> list[MatchingParameter] | list[MatchingBaseVariantParameter]:
25
24
  return self.matching_parameters
26
25
 
27
26
  @staticmethod
28
27
  def from_et(et_element: ElementTree.Element,
29
- doc_frags: List[OdxDocFragment]) -> "EcuVariantPattern":
28
+ doc_frags: list[OdxDocFragment]) -> "EcuVariantPattern":
30
29
 
31
30
  matching_parameters = [
32
31
  MatchingParameter.from_et(mp_el, doc_frags)
odxtools/element.py CHANGED
@@ -1,5 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import List, Optional
3
2
  from xml.etree import ElementTree
4
3
 
5
4
  from .description import Description
@@ -11,11 +10,11 @@ from .utils import dataclass_fields_asdict
11
10
  @dataclass
12
11
  class NamedElement:
13
12
  short_name: str
14
- long_name: Optional[str]
15
- description: Optional[Description]
13
+ long_name: str | None
14
+ description: Description | None
16
15
 
17
16
  @staticmethod
18
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "NamedElement":
17
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "NamedElement":
19
18
 
20
19
  return NamedElement(
21
20
  short_name=odxrequire(et_element.findtext("SHORT-NAME")),
@@ -27,11 +26,11 @@ class NamedElement:
27
26
  @dataclass
28
27
  class IdentifiableElement(NamedElement):
29
28
  odx_id: OdxLinkId
30
- oid: Optional[str]
29
+ oid: str | None
31
30
 
32
31
  @staticmethod
33
32
  def from_et(et_element: ElementTree.Element,
34
- doc_frags: List[OdxDocFragment]) -> "IdentifiableElement":
33
+ doc_frags: list[OdxDocFragment]) -> "IdentifiableElement":
35
34
 
36
35
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
37
36
 
odxtools/encodestate.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from .encoding import Encoding, get_string_encoding
7
7
  from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
@@ -41,20 +41,20 @@ class EncodeState:
41
41
  cursor_bit_position: int = 0
42
42
 
43
43
  #: If encoding a response: request that triggered the response
44
- triggering_request: Optional[bytes] = None
44
+ triggering_request: bytes | None = None
45
45
 
46
46
  #: Mapping from the short name of a length-key parameter to bit
47
47
  #: lengths (specified by LengthKeyParameter)
48
- length_keys: Dict[str, int] = field(default_factory=dict)
48
+ length_keys: dict[str, int] = field(default_factory=dict)
49
49
 
50
50
  #: Mapping from the short name of a table-key parameter to the
51
51
  #: short name of the corresponding row of the table (specified by
52
52
  #: TableKeyParameter)
53
- table_keys: Dict[str, str] = field(default_factory=dict)
53
+ table_keys: dict[str, str] = field(default_factory=dict)
54
54
 
55
55
  #: The cursor position where a given length- or table key is located
56
56
  #: in the PDU
57
- key_pos: Dict[str, int] = field(default_factory=dict)
57
+ key_pos: dict[str, int] = field(default_factory=dict)
58
58
 
59
59
  #: Flag whether we are currently the last parameter of the PDU
60
60
  #: (needed for MinMaxLengthType, EndOfPduField, etc.)
@@ -63,7 +63,7 @@ class EncodeState:
63
63
  #: list of parameters that have been encoded so far. The journal
64
64
  #: is used by some types of parameters which depend on the values of
65
65
  #: other parameters; e.g., environment data description parameters
66
- journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
66
+ journal: list[tuple["Parameter", ParameterValue | None]] = field(default_factory=list)
67
67
 
68
68
  #: If this is True, specifying unknown parameters for encoding
69
69
  #: will raise an OdxError exception in strict mode.
@@ -87,9 +87,9 @@ class EncodeState:
87
87
  internal_value: AtomicOdxType,
88
88
  bit_length: int,
89
89
  base_data_type: DataType,
90
- base_type_encoding: Optional[Encoding],
90
+ base_type_encoding: Encoding | None,
91
91
  is_highlow_byte_order: bool,
92
- used_mask: Optional[bytes],
92
+ used_mask: bytes | None,
93
93
  ) -> None:
94
94
  """Convert the internal_value to bytes and emplace this into the PDU"""
95
95
 
@@ -220,7 +220,7 @@ class EncodeState:
220
220
  odxraise(f"Illegal bit length for a float64 object ({bit_length})")
221
221
  bit_length = 64
222
222
 
223
- raw_value = float(internal_value)
223
+ raw_value = float(internal_value) # type: ignore[arg-type]
224
224
 
225
225
  # If the bit length is zero, encode an empty value
226
226
  if bit_length == 0:
@@ -260,8 +260,8 @@ class EncodeState:
260
260
 
261
261
  def emplace_bytes(self,
262
262
  new_data: bytes,
263
- obj_name: Optional[str] = None,
264
- obj_used_mask: Optional[bytes] = None) -> None:
263
+ obj_name: str | None = None,
264
+ obj_used_mask: bytes | None = None) -> None:
265
265
  if self.cursor_bit_position != 0:
266
266
  odxraise("EncodeState.emplace_bytes can only be called "
267
267
  "for a bit position of 0!", RuntimeError)
odxtools/encoding.py CHANGED
@@ -1,6 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import Enum
3
- from typing import Optional
4
3
 
5
4
  from .exceptions import odxraise
6
5
  from .odxtypes import DataType
@@ -23,8 +22,8 @@ class Encoding(Enum):
23
22
  NONE = "NONE"
24
23
 
25
24
 
26
- def get_string_encoding(base_data_type: DataType, base_type_encoding: Optional[Encoding],
27
- is_highlow_byte_order: bool) -> Optional[str]:
25
+ def get_string_encoding(base_data_type: DataType, base_type_encoding: Encoding | None,
26
+ is_highlow_byte_order: bool) -> str | None:
28
27
  """If the encoding is for a string, return the value for
29
28
  `str.encode()`/`str.decode()` to convert the string object
30
29
  to/from a byte array
odxtools/endofpdufield.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
+ from collections.abc import Sequence
2
3
  from dataclasses import dataclass
3
- from typing import List, Optional, Sequence
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -17,12 +17,12 @@ from .utils import dataclass_fields_asdict
17
17
  @dataclass
18
18
  class EndOfPduField(Field):
19
19
  """End of PDU fields are structures that are repeated until the end of the PDU"""
20
- max_number_of_items: Optional[int]
21
- min_number_of_items: Optional[int]
20
+ max_number_of_items: int | None
21
+ min_number_of_items: int | None
22
22
 
23
23
  @staticmethod
24
24
  def from_et(et_element: ElementTree.Element,
25
- doc_frags: List[OdxDocFragment]) -> "EndOfPduField":
25
+ doc_frags: list[OdxDocFragment]) -> "EndOfPduField":
26
26
  kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
27
27
 
28
28
  if (max_n_str := et_element.findtext("MAX-NUMBER-OF-ITEMS")) is not None:
@@ -40,7 +40,7 @@ class EndOfPduField(Field):
40
40
  **kwargs)
41
41
 
42
42
  @override
43
- def encode_into_pdu(self, physical_value: Optional[ParameterValue],
43
+ def encode_into_pdu(self, physical_value: ParameterValue | None,
44
44
  encode_state: EncodeState) -> None:
45
45
  odxassert(not encode_state.cursor_bit_position,
46
46
  "No bit position can be specified for end-of-pdu fields!")
@@ -72,7 +72,7 @@ class EndOfPduField(Field):
72
72
  orig_origin = decode_state.origin_byte_position
73
73
  decode_state.origin_byte_position = decode_state.cursor_byte_position
74
74
 
75
- result: List[ParameterValue] = []
75
+ result: list[ParameterValue] = []
76
76
  while decode_state.cursor_byte_position < len(decode_state.coded_message):
77
77
  # ATTENTION: the ODX specification is very misleading
78
78
  # here: it says that the item is repeated until the end of
@@ -0,0 +1,49 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+ from xml.etree import ElementTree
5
+
6
+ from .element import NamedElement
7
+ from .environmentdata import EnvironmentData
8
+ from .environmentdatadescription import EnvironmentDataDescription
9
+ from .exceptions import odxrequire
10
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
11
+ from .snrefcontext import SnRefContext
12
+ from .utils import dataclass_fields_asdict
13
+
14
+
15
+ @dataclass
16
+ class EnvDataConnector(NamedElement):
17
+ env_data_desc_ref: OdxLinkRef
18
+ env_data_snref: str
19
+
20
+ @property
21
+ def env_data_desc(self) -> EnvironmentDataDescription:
22
+ return self._env_data_desc
23
+
24
+ @property
25
+ def env_data(self) -> EnvironmentData:
26
+ return self._env_data
27
+
28
+ @staticmethod
29
+ def from_et(et_element: ElementTree.Element,
30
+ doc_frags: list[OdxDocFragment]) -> "EnvDataConnector":
31
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
32
+
33
+ env_data_desc_ref = odxrequire(
34
+ OdxLinkRef.from_et(et_element.find("ENV-DATA-DESC-REF"), doc_frags))
35
+ env_data_snref_el = odxrequire(et_element.find("ENV-DATA-SNREF"))
36
+ env_data_snref = odxrequire(env_data_snref_el.get("SHORT-NAME"))
37
+
38
+ return EnvDataConnector(
39
+ env_data_desc_ref=env_data_desc_ref, env_data_snref=env_data_snref, **kwargs)
40
+
41
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
42
+ return {}
43
+
44
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
45
+ self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref, EnvironmentDataDescription)
46
+
47
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
48
+ self._env_data = resolve_snref(self.env_data_snref, self._env_data_desc.env_datas,
49
+ EnvironmentData)
@@ -1,6 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List, Optional
4
3
  from xml.etree import ElementTree
5
4
 
6
5
  from .basicstructure import BasicStructure
@@ -20,12 +19,12 @@ class EnvironmentData(BasicStructure):
20
19
  sense, it is quite similar to NRC-CONST parameters.)
21
20
  """
22
21
 
23
- all_value: Optional[bool]
24
- dtc_values: List[int]
22
+ all_value: bool | None
23
+ dtc_values: list[int]
25
24
 
26
25
  @staticmethod
27
26
  def from_et(et_element: ElementTree.Element,
28
- doc_frags: List[OdxDocFragment]) -> "EnvironmentData":
27
+ doc_frags: list[OdxDocFragment]) -> "EnvironmentData":
29
28
  """Reads Environment Data from Diag Layer."""
30
29
  kwargs = dataclass_fields_asdict(BasicStructure.from_et(et_element, doc_frags))
31
30
 
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -35,18 +35,18 @@ class EnvironmentDataDescription(ComplexDop):
35
35
 
36
36
  """
37
37
 
38
- param_snref: Optional[str]
39
- param_snpathref: Optional[str]
38
+ param_snref: str | None
39
+ param_snpathref: str | None
40
40
 
41
41
  # in ODX 2.0.0, ENV-DATAS seems to be a mandatory
42
42
  # sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
43
43
  # present
44
44
  env_datas: NamedItemList[EnvironmentData]
45
- env_data_refs: List[OdxLinkRef]
45
+ env_data_refs: list[OdxLinkRef]
46
46
 
47
47
  @staticmethod
48
48
  def from_et(et_element: ElementTree.Element,
49
- doc_frags: List[OdxDocFragment]) -> "EnvironmentDataDescription":
49
+ doc_frags: list[OdxDocFragment]) -> "EnvironmentDataDescription":
50
50
  """Reads Environment Data Description from Diag Layer."""
51
51
  kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element, doc_frags))
52
52
 
@@ -78,7 +78,7 @@ class EnvironmentDataDescription(ComplexDop):
78
78
  env_data_refs=env_data_refs,
79
79
  **kwargs)
80
80
 
81
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
81
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
82
82
  odxlinks = {self.odx_id: self}
83
83
 
84
84
  if not self.env_data_refs:
@@ -105,7 +105,7 @@ class EnvironmentDataDescription(ComplexDop):
105
105
  ed._resolve_snrefs(context)
106
106
 
107
107
  @override
108
- def encode_into_pdu(self, physical_value: Optional[ParameterValue],
108
+ def encode_into_pdu(self, physical_value: ParameterValue | None,
109
109
  encode_state: EncodeState) -> None:
110
110
  """Convert a physical value into bytes and emplace them into a PDU.
111
111
  """
@@ -118,7 +118,7 @@ class EnvironmentDataDescription(ComplexDop):
118
118
  "descriptions via SNPATHREF is not supported yet")
119
119
  return None
120
120
 
121
- numerical_dtc_value: Optional[ParameterValue] = None
121
+ numerical_dtc_value: ParameterValue | None = None
122
122
  for prev_param, prev_param_value in reversed(encode_state.journal):
123
123
  if prev_param.short_name == self.param_snref:
124
124
  numerical_dtc_value = self._get_numerical_dtc_from_parameter(
@@ -165,7 +165,7 @@ class EnvironmentDataDescription(ComplexDop):
165
165
  "descriptions via SNPATHREF is not supported yet")
166
166
  return None
167
167
 
168
- numerical_dtc_value: Optional[ParameterValue] = None
168
+ numerical_dtc_value: ParameterValue | None = None
169
169
  for prev_param, prev_param_value in reversed(decode_state.journal):
170
170
  if prev_param.short_name == self.param_snref:
171
171
  numerical_dtc_value = self._get_numerical_dtc_from_parameter(
@@ -204,10 +204,10 @@ class EnvironmentDataDescription(ComplexDop):
204
204
  return result
205
205
 
206
206
  def _get_numerical_dtc_from_parameter(self, param: Parameter,
207
- param_value: Optional[ParameterValue]) -> int:
207
+ param_value: ParameterValue | None) -> int:
208
208
  if isinstance(param, ParameterWithDOP):
209
209
  dop = param.dop
210
- if not isinstance(dop, (DataObjectProperty, DtcDop)):
210
+ if not isinstance(dop, DataObjectProperty | DtcDop):
211
211
  odxraise(f"The DOP of the parameter referenced by environment data descriptions "
212
212
  f"must use either be DataObjectProperty or a DtcDop (encountered "
213
213
  f"{type(param).__name__} for parameter '{param.short_name}' "
odxtools/exceptions.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from typing import TYPE_CHECKING, NoReturn, Optional, Type, TypeVar
2
+ from typing import TYPE_CHECKING, NoReturn, TypeVar
3
3
 
4
4
  from .globals import logger
5
5
 
@@ -37,7 +37,7 @@ class OdxWarning(Warning):
37
37
  strict_mode = True
38
38
 
39
39
 
40
- def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxError) -> NoReturn:
40
+ def odxraise(message: str | None = None, error_type: type[Exception] = OdxError) -> NoReturn:
41
41
  """
42
42
  Raise an exception but only if in strict mode.
43
43
 
@@ -53,8 +53,8 @@ def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxErr
53
53
 
54
54
 
55
55
  def odxassert(condition: bool,
56
- message: Optional[str] = None,
57
- error_type: Type[Exception] = OdxError) -> None:
56
+ message: str | None = None,
57
+ error_type: type[Exception] = OdxError) -> None:
58
58
  """
59
59
  This method works similar as the build-in `assert` statement
60
60
 
@@ -72,7 +72,7 @@ def odxassert(condition: bool,
72
72
  T = TypeVar("T")
73
73
 
74
74
 
75
- def odxrequire(obj: Optional[T], message: Optional[str] = None) -> T:
75
+ def odxrequire(obj: T | None, message: str | None = None) -> T:
76
76
  """This function ensures that an object required by the ODX
77
77
  specification is actually present.
78
78
 
@@ -0,0 +1,22 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from xml.etree import ElementTree
4
+
5
+ from .element import IdentifiableElement
6
+ from .exceptions import odxrequire
7
+ from .odxlink import OdxDocFragment
8
+ from .utils import dataclass_fields_asdict
9
+
10
+
11
+ @dataclass
12
+ class ExternalAccessMethod(IdentifiableElement):
13
+ method: str
14
+
15
+ @staticmethod
16
+ def from_et(et_element: ElementTree.Element,
17
+ doc_frags: list[OdxDocFragment]) -> "ExternalAccessMethod":
18
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
19
+
20
+ method = odxrequire(et_element.findtext("METHOD"))
21
+
22
+ return ExternalAccessMethod(method=method, **kwargs)
@@ -0,0 +1,23 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from xml.etree import ElementTree
4
+
5
+ from .exceptions import odxrequire
6
+ from .odxlink import OdxDocFragment
7
+
8
+
9
+ @dataclass
10
+ class ExternalDoc:
11
+ description: str | None
12
+ href: str
13
+
14
+ @staticmethod
15
+ def from_et(et_element: ElementTree.Element | None,
16
+ doc_frags: list[OdxDocFragment]) -> Optional["ExternalDoc"]:
17
+ if et_element is None:
18
+ return None
19
+
20
+ description = et_element.text
21
+ href = odxrequire(et_element.get("HREF"))
22
+
23
+ return ExternalDoc(description=description, href=href)
odxtools/field.py CHANGED
@@ -1,6 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List, Optional
4
3
  from xml.etree import ElementTree
5
4
 
6
5
  from .basicstructure import BasicStructure
@@ -15,11 +14,11 @@ from .utils import dataclass_fields_asdict
15
14
 
16
15
  @dataclass
17
16
  class Field(ComplexDop):
18
- structure_ref: Optional[OdxLinkRef]
19
- structure_snref: Optional[str]
20
- env_data_desc_ref: Optional[OdxLinkRef]
21
- env_data_desc_snref: Optional[str]
22
- is_visible_raw: Optional[bool]
17
+ structure_ref: OdxLinkRef | None
18
+ structure_snref: str | None
19
+ env_data_desc_ref: OdxLinkRef | None
20
+ env_data_desc_snref: str | None
21
+ is_visible_raw: bool | None
23
22
 
24
23
  @property
25
24
  def structure(self) -> BasicStructure:
@@ -31,7 +30,7 @@ class Field(ComplexDop):
31
30
  return self.is_visible_raw in (None, True)
32
31
 
33
32
  @staticmethod
34
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Field":
33
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Field":
35
34
  kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element, doc_frags))
36
35
 
37
36
  structure_ref = OdxLinkRef.from_et(et_element.find("BASIC-STRUCTURE-REF"), doc_frags)
@@ -54,8 +53,8 @@ class Field(ComplexDop):
54
53
  **kwargs)
55
54
 
56
55
  def __post_init__(self) -> None:
57
- self._structure: Optional[BasicStructure] = None
58
- self._env_data_desc: Optional[EnvironmentDataDescription] = None
56
+ self._structure: BasicStructure | None = None
57
+ self._env_data_desc: EnvironmentDataDescription | None = None
59
58
  num_struct_refs = 0 if self.structure_ref is None else 1
60
59
  num_struct_refs += 0 if self.structure_snref is None else 1
61
60
 
@@ -87,5 +86,5 @@ class Field(ComplexDop):
87
86
  self._env_data_desc = resolve_snref(self.env_data_desc_snref, ddds.env_data_descs,
88
87
  EnvironmentDataDescription)
89
88
 
90
- def get_static_bit_length(self) -> Optional[int]:
89
+ def get_static_bit_length(self) -> int | None:
91
90
  return None