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
odxtools/tablerow.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass, fields
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
3
+ from typing import TYPE_CHECKING, Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
@@ -33,22 +33,22 @@ class TableRow(IdentifiableElement):
33
33
  # The spec mandates that either a structure or a non-complex DOP
34
34
  # must be referenced here, i.e., exactly one of the four
35
35
  # attributes below is not None
36
- dop_ref: Optional[OdxLinkRef]
37
- dop_snref: Optional[str]
38
- structure_ref: Optional[OdxLinkRef]
39
- structure_snref: Optional[str]
40
-
41
- sdgs: List[SpecialDataGroup]
42
- audience: Optional[Audience]
43
- functional_class_refs: List[OdxLinkRef]
44
- state_transition_refs: List[StateTransitionRef]
45
- pre_condition_state_refs: List[PreConditionStateRef]
46
- admin_data: Optional[AdminData]
47
-
48
- is_executable_raw: Optional[bool]
49
- semantic: Optional[str]
50
- is_mandatory_raw: Optional[bool]
51
- is_final_raw: Optional[bool]
36
+ dop_ref: OdxLinkRef | None
37
+ dop_snref: str | None
38
+ structure_ref: OdxLinkRef | None
39
+ structure_snref: str | None
40
+
41
+ sdgs: list[SpecialDataGroup]
42
+ audience: Audience | None
43
+ functional_class_refs: list[OdxLinkRef]
44
+ state_transition_refs: list[StateTransitionRef]
45
+ pre_condition_state_refs: list[PreConditionStateRef]
46
+ admin_data: AdminData | None
47
+
48
+ is_executable_raw: bool | None
49
+ semantic: str | None
50
+ is_mandatory_raw: bool | None
51
+ is_final_raw: bool | None
52
52
 
53
53
  @property
54
54
  def table(self) -> "Table":
@@ -57,16 +57,16 @@ class TableRow(IdentifiableElement):
57
57
  # the value of the key expressed in the type represented by the
58
58
  # referenced DOP
59
59
  @property
60
- def key(self) -> Optional[AtomicOdxType]:
60
+ def key(self) -> AtomicOdxType | None:
61
61
  return self._key
62
62
 
63
63
  @property
64
- def dop(self) -> Optional[DataObjectProperty]:
64
+ def dop(self) -> DataObjectProperty | None:
65
65
  """The data object property object resolved by dop_ref."""
66
66
  return self._dop
67
67
 
68
68
  @property
69
- def structure(self) -> Optional[Structure]:
69
+ def structure(self) -> Structure | None:
70
70
  """The structure associated with this table row."""
71
71
  return self._structure
72
72
 
@@ -87,12 +87,12 @@ class TableRow(IdentifiableElement):
87
87
  return self.is_final_raw is True
88
88
 
89
89
  @staticmethod
90
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> Any:
90
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> Any:
91
91
  raise RuntimeError(
92
92
  "Calling TableRow.from_et() is not allowed. Use TableRow.tablerow_from_et().")
93
93
 
94
94
  @staticmethod
95
- def tablerow_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
95
+ def tablerow_from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment], *,
96
96
  table_ref: OdxLinkRef) -> "TableRow":
97
97
  """Reads a TABLE-ROW."""
98
98
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
@@ -100,12 +100,12 @@ class TableRow(IdentifiableElement):
100
100
  key_raw = odxrequire(et_element.findtext("KEY"))
101
101
 
102
102
  dop_ref = OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), doc_frags)
103
- dop_snref: Optional[str] = None
103
+ dop_snref: str | None = None
104
104
  if (dop_snref_elem := et_element.find("DATA-OBJECT-PROP-SNREF")) is not None:
105
105
  dop_snref = dop_snref_elem.attrib["SHORT-NAME"]
106
106
 
107
107
  structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
108
- structure_snref: Optional[str] = None
108
+ structure_snref: str | None = None
109
109
  if (structure_snref_elem := et_element.find("STRUCTURE-SNREF")) is not None:
110
110
  structure_snref = structure_snref_elem.attrib["SHORT-NAME"]
111
111
 
@@ -159,8 +159,8 @@ class TableRow(IdentifiableElement):
159
159
  **kwargs)
160
160
 
161
161
  def __post_init__(self) -> None:
162
- self._dop: Optional[DataObjectProperty] = None
163
- self._structure: Optional[Structure] = None
162
+ self._dop: DataObjectProperty | None = None
163
+ self._structure: Structure | None = None
164
164
 
165
165
  n = sum([0 if x is None else 1 for x in (self.dop_ref, self.dop_snref)])
166
166
  odxassert(
@@ -174,7 +174,7 @@ class TableRow(IdentifiableElement):
174
174
  f"Table row {self.short_name}: The structure can either be defined using ODXLINK or SNREF but not both."
175
175
  )
176
176
 
177
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
177
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
178
178
  result = {self.odx_id: self}
179
179
 
180
180
  for sdg in self.sdgs:
@@ -199,7 +199,7 @@ class TableRow(IdentifiableElement):
199
199
 
200
200
  if self.dop_ref is not None:
201
201
  self._dop = odxlinks.resolve(self.dop_ref)
202
- if not isinstance(self._dop, (DataObjectProperty, DtcDop)):
202
+ if not isinstance(self._dop, DataObjectProperty | DtcDop):
203
203
  odxraise("The DOP-REF of TABLE-ROWs must reference a simple DOP!")
204
204
  if self.structure_ref is not None:
205
205
  self._structure = odxlinks.resolve(self.structure_ref, Structure)
@@ -243,7 +243,7 @@ class TableRow(IdentifiableElement):
243
243
  self._structure = resolve_snref(self.structure_snref, ddd_spec.structures, Structure)
244
244
  if self.dop_snref is not None:
245
245
  self._dop = resolve_snref(self.dop_snref, ddd_spec.data_object_props)
246
- if not isinstance(self._dop, (DataObjectProperty, DtcDop)):
246
+ if not isinstance(self._dop, DataObjectProperty | DtcDop):
247
247
  odxraise("The DOP-SNREF of TABLE-ROWs must reference a simple DOP!")
248
248
 
249
249
  if self.audience is not None:
@@ -258,7 +258,7 @@ class TableRow(IdentifiableElement):
258
258
  for pc_ref in self.pre_condition_state_refs:
259
259
  pc_ref._resolve_snrefs(context)
260
260
 
261
- def __reduce__(self) -> Tuple[Any, ...]:
261
+ def __reduce__(self) -> tuple[Any, ...]:
262
262
  """This ensures that the object can be correctly reconstructed during unpickling."""
263
263
  state = self.__dict__.copy()
264
264
  return self.__class__, tuple([getattr(self, x.name) for x in fields(self)]), state
@@ -0,0 +1,46 @@
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 .exceptions import odxrequire
8
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
9
+ from .snrefcontext import SnRefContext
10
+ from .table import Table
11
+ from .tablerow import TableRow
12
+ from .utils import dataclass_fields_asdict
13
+
14
+
15
+ @dataclass
16
+ class TableRowConnector(NamedElement):
17
+ table_ref: OdxLinkRef
18
+ table_row_snref: str
19
+
20
+ @property
21
+ def table(self) -> Table:
22
+ return self._table
23
+
24
+ @property
25
+ def table_row(self) -> TableRow:
26
+ return self._table_row
27
+
28
+ @staticmethod
29
+ def from_et(et_element: ElementTree.Element,
30
+ doc_frags: list[OdxDocFragment]) -> "TableRowConnector":
31
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
32
+
33
+ table_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags))
34
+ table_row_snref_el = odxrequire(et_element.find("TABLE-ROW-SNREF"))
35
+ table_row_snref = odxrequire(table_row_snref_el.get("SHORT-NAME"))
36
+
37
+ return TableRowConnector(table_ref=table_ref, table_row_snref=table_row_snref, **kwargs)
38
+
39
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
40
+ return {}
41
+
42
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
43
+ self._table = odxlinks.resolve(self.table_ref, Table)
44
+
45
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
46
+ self._table_row = resolve_snref(self.table_row_snref, self._table.table_rows, TableRow)
odxtools/teammember.py CHANGED
@@ -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 .element import IdentifiableElement
@@ -12,17 +12,17 @@ from .utils import dataclass_fields_asdict
12
12
 
13
13
  @dataclass
14
14
  class TeamMember(IdentifiableElement):
15
- roles: List[str]
16
- department: Optional[str]
17
- address: Optional[str]
18
- zipcode: Optional[str] # the tag for this is "ZIP", but `zip` is a keyword in python
19
- city: Optional[str]
20
- phone: Optional[str]
21
- fax: Optional[str]
22
- email: Optional[str]
15
+ roles: list[str]
16
+ department: str | None
17
+ address: str | None
18
+ zipcode: str | None # the tag for this is "ZIP", but `zip` is a keyword in python
19
+ city: str | None
20
+ phone: str | None
21
+ fax: str | None
22
+ email: str | None
23
23
 
24
24
  @staticmethod
25
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "TeamMember":
25
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "TeamMember":
26
26
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
27
27
 
28
28
  roles = [odxrequire(role_elem.text) for role_elem in et_element.iterfind("ROLES/ROLE")]
@@ -45,7 +45,7 @@ class TeamMember(IdentifiableElement):
45
45
  email=email,
46
46
  **kwargs)
47
47
 
48
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
48
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
49
49
  result = {self.odx_id: self}
50
50
 
51
51
  return result
@@ -8,7 +8,8 @@
8
8
 
9
9
  {%- macro printPosResponseSuppressible(prs) -%}
10
10
  <POS-RESPONSE-SUPPRESSABLE>
11
- <BIT-MASK>{{ hex(prs.bit_mask).lower() }}</BIT-MASK>
11
+ {%- set num_nibbles = (prs.bit_mask.bit_length() + 7) // 8 * 2 %}
12
+ <BIT-MASK>{{ ("%%0%dX" | format(num_nibbles | int)) | format(prs.bit_mask | int) }}</BIT-MASK>
12
13
  {%- if prs.coded_const_snref is not none %}
13
14
  <CODED-CONST-SNREF SHORT-NAME="{{ prs.coded_const_snref }}" />
14
15
  {%- endif %}
@@ -0,0 +1,8 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class Termination(Enum):
6
+ END_OF_PDU = "END-OF-PDU"
7
+ ZERO = "ZERO"
8
+ HEX_FF = "HEX-FF"
odxtools/text.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 .odxlink import OdxDocFragment
@@ -8,10 +7,10 @@ from .odxlink import OdxDocFragment
8
7
  @dataclass
9
8
  class Text:
10
9
  text: str
11
- text_identifier: Optional[str]
10
+ text_identifier: str | None
12
11
 
13
12
  @staticmethod
14
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Text":
13
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Text":
15
14
  # Extract the contents of the tag as a string.
16
15
  raw_string = et_element.text or ""
17
16
  for e in et_element:
odxtools/transmode.py ADDED
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class TransMode(Enum):
6
+ SEND_ONLY = "SEND-ONLY"
7
+ RECEIVE_ONLY = "RECEIVE-ONLY"
8
+ SEND_AND_RECEIVE = "SEND-AND-RECEIVE"
9
+ SEND_OR_RECEIVE = "SEND-OR-RECEIVE"
odxtools/uds.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import IntEnum
3
3
  from itertools import chain
4
- from typing import Optional
5
4
 
6
5
  import odxtools.obd as obd
7
6
 
@@ -82,7 +81,7 @@ _sid_to_name = {
82
81
  SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore[misc]
83
82
 
84
83
 
85
- def sid_to_name(sid: int) -> Optional[str]:
84
+ def sid_to_name(sid: int) -> str | None:
86
85
  if sid in _sid_to_name:
87
86
  return _sid_to_name[sid]
88
87
  elif 0x81 <= sid and sid <= 0x82:
@@ -142,7 +141,7 @@ def negative_response_id(service_id: int) -> int:
142
141
  return NegativeResponseId
143
142
 
144
143
 
145
- def is_response_pending(telegram_payload: bytes, request_sid: Optional[int] = None) -> bool:
144
+ def is_response_pending(telegram_payload: bytes, request_sid: int | None = None) -> bool:
146
145
  # "response pending" responses exhibit at least three bytes
147
146
  if len(telegram_payload) < 3:
148
147
  return False
odxtools/unit.py CHANGED
@@ -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 .element import IdentifiableElement
@@ -53,21 +53,21 @@ class Unit(IdentifiableElement):
53
53
  ```
54
54
  """
55
55
  display_name: str
56
- factor_si_to_unit: Optional[float]
57
- offset_si_to_unit: Optional[float]
58
- physical_dimension_ref: Optional[OdxLinkRef]
56
+ factor_si_to_unit: float | None
57
+ offset_si_to_unit: float | None
58
+ physical_dimension_ref: OdxLinkRef | None
59
59
 
60
60
  @property
61
- def physical_dimension(self) -> Optional[PhysicalDimension]:
61
+ def physical_dimension(self) -> PhysicalDimension | None:
62
62
  return self._physical_dimension
63
63
 
64
64
  @staticmethod
65
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Unit":
65
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Unit":
66
66
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
67
67
 
68
68
  display_name = odxrequire(et_element.findtext("DISPLAY-NAME"))
69
69
 
70
- def read_optional_float(element: ElementTree.Element, name: str) -> Optional[float]:
70
+ def read_optional_float(element: ElementTree.Element, name: str) -> float | None:
71
71
  if (elem_str := element.findtext(name)) is not None:
72
72
  return float(elem_str)
73
73
  else:
@@ -85,11 +85,11 @@ class Unit(IdentifiableElement):
85
85
  physical_dimension_ref=physical_dimension_ref,
86
86
  **kwargs)
87
87
 
88
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
88
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
89
89
  return {self.odx_id: self}
90
90
 
91
91
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
92
- self._physical_dimension: Optional[PhysicalDimension] = None
92
+ self._physical_dimension: PhysicalDimension | None = None
93
93
  if self.physical_dimension_ref:
94
94
  self._physical_dimension = odxlinks.resolve(self.physical_dimension_ref,
95
95
  PhysicalDimension)
odxtools/unitgroup.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from enum import Enum
4
- from typing import Any, Dict, List, Optional, cast
3
+ from typing import Any, cast
5
4
  from xml.etree import ElementTree
6
5
 
7
6
  from .element import NamedElement
@@ -10,14 +9,10 @@ from .nameditemlist import NamedItemList
10
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
10
  from .snrefcontext import SnRefContext
12
11
  from .unit import Unit
12
+ from .unitgroupcategory import UnitGroupCategory
13
13
  from .utils import dataclass_fields_asdict
14
14
 
15
15
 
16
- class UnitGroupCategory(Enum):
17
- COUNTRY = "COUNTRY"
18
- EQUIV_UNITS = "EQUIV-UNITS"
19
-
20
-
21
16
  @dataclass
22
17
  class UnitGroup(NamedElement):
23
18
  """A group of units.
@@ -25,15 +20,15 @@ class UnitGroup(NamedElement):
25
20
  There are two categories of groups: COUNTRY and EQUIV-UNITS.
26
21
  """
27
22
  category: UnitGroupCategory
28
- unit_refs: List[OdxLinkRef]
29
- oid: Optional[str]
23
+ unit_refs: list[OdxLinkRef]
24
+ oid: str | None
30
25
 
31
26
  @property
32
27
  def units(self) -> NamedItemList[Unit]:
33
28
  return self._units
34
29
 
35
30
  @staticmethod
36
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "UnitGroup":
31
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "UnitGroup":
37
32
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
38
33
 
39
34
  category_str = odxrequire(et_element.findtext("CATEGORY"))
@@ -51,7 +46,7 @@ class UnitGroup(NamedElement):
51
46
 
52
47
  return UnitGroup(category=category, unit_refs=unit_refs, oid=oid, **kwargs)
53
48
 
54
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
49
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
55
50
  return {}
56
51
 
57
52
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -0,0 +1,7 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class UnitGroupCategory(Enum):
6
+ COUNTRY = "COUNTRY"
7
+ EQUIV_UNITS = "EQUIV-UNITS"
odxtools/unitspec.py CHANGED
@@ -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 .admindata import AdminData
@@ -25,11 +25,11 @@ class UnitSpec:
25
25
  The following odx elements are not internalized: ADMIN-DATA, SDGS
26
26
  """
27
27
 
28
- admin_data: Optional[AdminData]
28
+ admin_data: AdminData | None
29
29
  unit_groups: NamedItemList[UnitGroup]
30
30
  units: NamedItemList[Unit]
31
31
  physical_dimensions: NamedItemList[PhysicalDimension]
32
- sdgs: List[SpecialDataGroup]
32
+ sdgs: list[SpecialDataGroup]
33
33
 
34
34
  def __post_init__(self) -> None:
35
35
  self.unit_groups = NamedItemList(self.unit_groups)
@@ -37,7 +37,7 @@ class UnitSpec:
37
37
  self.physical_dimensions = NamedItemList(self.physical_dimensions)
38
38
 
39
39
  @staticmethod
40
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "UnitSpec":
40
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "UnitSpec":
41
41
 
42
42
  admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
43
43
  unit_groups = NamedItemList([
@@ -60,8 +60,8 @@ class UnitSpec:
60
60
  physical_dimensions=physical_dimensions,
61
61
  sdgs=sdgs)
62
62
 
63
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
64
- odxlinks: Dict[OdxLinkId, Any] = {}
63
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
64
+ odxlinks: dict[OdxLinkId, Any] = {}
65
65
  for unit in self.units:
66
66
  odxlinks.update(unit._build_odxlinks())
67
67
  for dim in self.physical_dimensions:
odxtools/usage.py ADDED
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class Usage(Enum):
6
+ ECU_SOFTWARE = "ECU-SOFTWARE"
7
+ ECU_COMM = "ECU-COMM"
8
+ APPLICATION = "APPLICATION"
9
+ TESTER = "TESTER"
odxtools/utils.py CHANGED
@@ -1,7 +1,10 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import dataclasses
3
3
  import re
4
- from typing import TYPE_CHECKING, Any, Dict, Optional
4
+ from typing import TYPE_CHECKING, Any, Optional
5
+ from xml.etree import ElementTree
6
+
7
+ from .exceptions import odxraise
5
8
 
6
9
  if TYPE_CHECKING:
7
10
  from .database import Database
@@ -9,6 +12,32 @@ if TYPE_CHECKING:
9
12
  from .snrefcontext import SnRefContext
10
13
 
11
14
 
15
+ def read_hex_binary(et_element: ElementTree.Element | None) -> int | None:
16
+ """Convert the contents of an xsd:hexBinary to an integer
17
+ """
18
+ if et_element is None:
19
+ return None
20
+
21
+ if (bytes_str := et_element.text) is None:
22
+ # tag exists but is immediately terminated ("<FOO />"). we
23
+ # treat this like an empty string.
24
+ return 0
25
+
26
+ # The XSD uses the type xsd:hexBinary and xsd:hexBinary allows for
27
+ # leading/trailing whitespace and empty strings whilst `int(x,
28
+ # 16)` raises an exception if one of these things happen.
29
+ bytes_str = bytes_str.strip()
30
+ if len(bytes_str) == 0:
31
+ return 0
32
+
33
+ try:
34
+ return int(bytes_str, 16)
35
+ except Exception as e:
36
+ odxraise(f"Caught exception while parsing hex string `{bytes_str}`"
37
+ f" of {et_element.tag}: {e}")
38
+ return None
39
+
40
+
12
41
  def retarget_snrefs(database: "Database",
13
42
  diag_layer: "DiagLayer",
14
43
  context: Optional["SnRefContext"] = None) -> None:
@@ -47,7 +76,7 @@ def retarget_snrefs(database: "Database",
47
76
  retarget_snrefs(database, pr.layer, context)
48
77
 
49
78
 
50
- def dataclass_fields_asdict(obj: Any) -> Dict[str, Any]:
79
+ def dataclass_fields_asdict(obj: Any) -> dict[str, Any]:
51
80
  """Extract all attributes from a dataclass object that are fields.
52
81
 
53
82
  This is a non-recursive version of `dataclasses.asdict()`. Its
odxtools/validtype.py ADDED
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class ValidType(Enum):
6
+ VALID = "VALID"
7
+ NOT_VALID = "NOT-VALID"
8
+ NOT_DEFINED = "NOT-DEFINED"
9
+ NOT_AVAILABLE = "NOT-AVAILABLE"
odxtools/variablegroup.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import typing
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, List, runtime_checkable
4
+ from typing import TYPE_CHECKING, runtime_checkable
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .element import IdentifiableElement, NamedElement
@@ -26,7 +26,7 @@ class VariableGroup(IdentifiableElement):
26
26
 
27
27
  @staticmethod
28
28
  def from_et(et_element: ElementTree.Element,
29
- doc_frags: List[OdxDocFragment]) -> "VariableGroup":
29
+ doc_frags: list[OdxDocFragment]) -> "VariableGroup":
30
30
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
31
31
 
32
32
  return VariableGroup(**kwargs)
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
+ from collections.abc import Generator
2
3
  from copy import copy
3
4
  from enum import Enum
4
- from typing import Dict, Generator, List, Optional, Tuple, Union
5
5
 
6
6
  from .diaglayers.basevariant import BaseVariant
7
7
  from .diaglayers.ecuvariant import EcuVariant
@@ -56,18 +56,18 @@ class VariantMatcher:
56
56
  MATCH = 2
57
57
 
58
58
  def __init__(self,
59
- variant_candidates: Union[List[EcuVariant], List[BaseVariant]],
59
+ variant_candidates: list[EcuVariant] | list[BaseVariant],
60
60
  use_cache: bool = True):
61
61
 
62
62
  self.variant_candidates = variant_candidates
63
63
  self.use_cache = use_cache
64
- self.req_resp_cache: Dict[bytes, bytes] = {}
65
- self._recent_ident_response: Optional[bytes] = None
64
+ self.req_resp_cache: dict[bytes, bytes] = {}
65
+ self._recent_ident_response: bytes | None = None
66
66
 
67
67
  self._state = VariantMatcher.State.PENDING
68
- self._matching_variant: Optional[Union[EcuVariant, BaseVariant]] = None
68
+ self._matching_variant: EcuVariant | BaseVariant | None = None
69
69
 
70
- def request_loop(self) -> Generator[Tuple[bool, bytes], None, None]:
70
+ def request_loop(self) -> Generator[tuple[bool, bytes], None, None]:
71
71
  """The request loop yielding tuples of byte sequences of
72
72
  requests and the whether physical addressing ought to be used
73
73
  to send them
@@ -86,7 +86,7 @@ class VariantMatcher:
86
86
 
87
87
  self._matching_variant = None
88
88
  for variant in self.variant_candidates:
89
- variant_patterns: Union[List[EcuVariantPattern], List[BaseVariantPattern]]
89
+ variant_patterns: list[EcuVariantPattern] | list[BaseVariantPattern]
90
90
  if isinstance(variant, EcuVariant):
91
91
  variant_patterns = variant.ecu_variant_patterns
92
92
  elif isinstance(variant, BaseVariant):
@@ -158,13 +158,13 @@ class VariantMatcher:
158
158
  return self._state == VariantMatcher.State.MATCH
159
159
 
160
160
  @property
161
- def matching_variant(self) -> Optional[Union[EcuVariant, BaseVariant]]:
161
+ def matching_variant(self) -> EcuVariant | BaseVariant | None:
162
162
  """Returns the matched, i.e., active ecu variant if such a variant has been found."""
163
163
  return self._matching_variant
164
164
 
165
165
  def _ident_response_matches(
166
166
  self,
167
- variant: Union[EcuVariant, BaseVariant],
167
+ variant: EcuVariant | BaseVariant,
168
168
  matching_param: MatchingParameter,
169
169
  response_bytes: bytes,
170
170
  ) -> bool:
@@ -175,7 +175,7 @@ class VariantMatcher:
175
175
 
176
176
  # ISO 22901 requires that snref or snpathref is resolvable in
177
177
  # at least one POS-RESPONSE or NEG-RESPONSE
178
- all_responses: List[Response] = []
178
+ all_responses: list[Response] = []
179
179
  all_responses.extend(service.positive_responses)
180
180
  all_responses.extend(service.negative_responses)
181
181
  all_responses.extend(variant.global_negative_responses)
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, List, Union
3
+ from typing import TYPE_CHECKING
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .exceptions import odxraise
@@ -26,7 +26,7 @@ class VariantPattern:
26
26
  """
27
27
 
28
28
  def get_matching_parameters(
29
- self) -> Union[List["MatchingParameter"], List["MatchingBaseVariantParameter"]]:
29
+ self) -> list["MatchingParameter"] | list["MatchingBaseVariantParameter"]:
30
30
  odxraise(
31
31
  f"VariantPattern subclass `{type(self).__name__}` does not "
32
32
  f"implement `.get_match_parameters()`", RuntimeError)
@@ -34,5 +34,5 @@ class VariantPattern:
34
34
 
35
35
  @staticmethod
36
36
  def from_et(et_element: ElementTree.Element,
37
- doc_frags: List[OdxDocFragment]) -> "VariantPattern":
37
+ doc_frags: list[OdxDocFragment]) -> "VariantPattern":
38
38
  return VariantPattern()
odxtools/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '9.6.1'
21
- __version_tuple__ = version_tuple = (9, 6, 1)
20
+ __version__ = version = '10.0.0'
21
+ __version_tuple__ = version_tuple = (10, 0, 0)