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
@@ -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
@@ -16,11 +16,11 @@ class FunctionalClass(IdentifiableElement):
16
16
  Corresponds to FUNCT-CLASS.
17
17
  """
18
18
 
19
- admin_data: Optional[AdminData]
19
+ admin_data: AdminData | None
20
20
 
21
21
  @staticmethod
22
22
  def from_et(et_element: ElementTree.Element,
23
- doc_frags: List[OdxDocFragment]) -> "FunctionalClass":
23
+ doc_frags: list[OdxDocFragment]) -> "FunctionalClass":
24
24
 
25
25
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
26
26
 
@@ -28,7 +28,7 @@ class FunctionalClass(IdentifiableElement):
28
28
 
29
29
  return FunctionalClass(admin_data=admin_data, **kwargs)
30
30
 
31
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
31
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
32
32
  return {self.odx_id: self}
33
33
 
34
34
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
odxtools/inputparam.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 deprecation import deprecated
@@ -15,10 +15,10 @@ from .utils import dataclass_fields_asdict
15
15
 
16
16
  @dataclass
17
17
  class InputParam(NamedElement):
18
- physical_default_value: Optional[str]
18
+ physical_default_value: str | None
19
19
  dop_base_ref: OdxLinkRef
20
- oid: Optional[str]
21
- semantic: Optional[str]
20
+ oid: str | None
21
+ semantic: str | None
22
22
 
23
23
  @property
24
24
  def dop(self) -> DopBase:
@@ -30,7 +30,7 @@ class InputParam(NamedElement):
30
30
  return self._dop
31
31
 
32
32
  @staticmethod
33
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "InputParam":
33
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "InputParam":
34
34
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
35
35
 
36
36
  physical_default_value = et_element.findtext("PHYSICAL-DEFAULT-VALUE")
@@ -46,7 +46,7 @@ class InputParam(NamedElement):
46
46
  semantic=semantic,
47
47
  **kwargs)
48
48
 
49
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
49
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
50
50
  return {}
51
51
 
52
52
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -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 .compumethods.limit import Limit
@@ -16,14 +15,14 @@ class InternalConstr:
16
15
 
17
16
  # TODO: Enforce the internal and physical constraints.
18
17
 
19
- lower_limit: Optional[Limit]
20
- upper_limit: Optional[Limit]
21
- scale_constrs: List[ScaleConstr]
18
+ lower_limit: Limit | None
19
+ upper_limit: Limit | None
20
+ scale_constrs: list[ScaleConstr]
22
21
 
23
22
  value_type: DataType
24
23
 
25
24
  @staticmethod
26
- def constr_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
25
+ def constr_from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment], *,
27
26
  value_type: DataType) -> "InternalConstr":
28
27
 
29
28
  lower_limit = Limit.limit_from_et(
@@ -4,9 +4,10 @@
4
4
  import asyncio
5
5
  import re
6
6
  import sys
7
+ from collections.abc import AsyncGenerator, Iterable
7
8
  from enum import IntEnum
8
9
  from io import TextIOBase
9
- from typing import AsyncGenerator, Iterable, List, Optional, TextIO, Tuple, Union
10
+ from typing import TextIO
10
11
 
11
12
  import bitstruct
12
13
  import can
@@ -32,7 +33,7 @@ class IsoTpStateMachine:
32
33
  can_fd_log_frame_re = re.compile(
33
34
  "\\([0-9.]*\\) *([a-zA-Z0-9_-]*) ([0-9A-Fa-f]+)##[0-9A-Fa-f]([0-9A-Fa-f]+)")
34
35
 
35
- def __init__(self, can_rx_ids: Union[int, List[int]]):
36
+ def __init__(self, can_rx_ids: int | list[int]):
36
37
  if isinstance(can_rx_ids, int):
37
38
  can_rx_ids = [can_rx_ids]
38
39
 
@@ -40,10 +41,10 @@ class IsoTpStateMachine:
40
41
  assert isinstance(self._can_rx_ids, list)
41
42
 
42
43
  self._telegram_specified_len = [0] * len(can_rx_ids)
43
- self._telegram_data: List[Optional[bytearray]] = [None] * len(can_rx_ids)
44
+ self._telegram_data: list[bytearray | None] = [None] * len(can_rx_ids)
44
45
  self._telegram_last_rx_fragment_idx = [0] * len(can_rx_ids)
45
46
 
46
- def decode_rx_frame(self, rx_id: int, data: bytes) -> Iterable[Tuple[int, bytes]]:
47
+ def decode_rx_frame(self, rx_id: int, data: bytes) -> Iterable[tuple[int, bytes]]:
47
48
  """Handle the ISO-TP state transitions caused by a CAN frame.
48
49
 
49
50
  E.g., add some data to a telegram, etc. Returns a generator of
@@ -114,8 +115,8 @@ class IsoTpStateMachine:
114
115
  else:
115
116
  self.on_frame_type_error(telegram_idx, frame_type)
116
117
 
117
- async def read_telegrams(self, bus: Union[can.BusABC,
118
- TextIO]) -> AsyncGenerator[Tuple[int, bytes], None]:
118
+ async def read_telegrams(self,
119
+ bus: can.BusABC | TextIO) -> AsyncGenerator[tuple[int, bytes], None]:
119
120
  """This is equivalent to the :py:meth:`file.readlines()` method, but
120
121
  it yields ISO-TP telegrams instead of lines.
121
122
 
@@ -184,7 +185,7 @@ class IsoTpStateMachine:
184
185
  """
185
186
  return self._can_rx_ids[telegram_idx]
186
187
 
187
- def telegram_data(self, telegram_idx: int) -> Optional[bytes]:
188
+ def telegram_data(self, telegram_idx: int) -> bytes | None:
188
189
  """Given a Telegram index, returns the data received for this telegram
189
190
  so far.
190
191
 
@@ -233,8 +234,8 @@ class IsoTpActiveDecoder(IsoTpStateMachine):
233
234
 
234
235
  def __init__(self,
235
236
  can_bus: can.BusABC,
236
- can_rx_ids: List[int],
237
- can_tx_ids: List[int],
237
+ can_rx_ids: list[int],
238
+ can_tx_ids: list[int],
238
239
  padding_size: int = 0,
239
240
  padding_value: int = 0xAA):
240
241
  self._can_bus = can_bus
@@ -251,8 +252,8 @@ class IsoTpActiveDecoder(IsoTpStateMachine):
251
252
  assert len(self._can_rx_ids) == len(self._can_tx_ids)
252
253
  assert set(self._can_rx_ids).isdisjoint(set(self._can_tx_ids)) # correct?
253
254
 
254
- self._block_size: List[Optional[int]] = [None] * len(self._can_rx_ids)
255
- self._frames_received: List[Optional[int]] = [None] * len(self._can_rx_ids)
255
+ self._block_size: list[int | None] = [None] * len(self._can_rx_ids)
256
+ self._frames_received: list[int | None] = [None] * len(self._can_rx_ids)
256
257
 
257
258
  def can_tx_id(self, telegram_idx: int) -> int:
258
259
  """Given a Telegram index, returns the CAN ID for sending data.
@@ -1,6 +1,5 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
4
3
  from xml.etree import ElementTree
5
4
 
6
5
  from typing_extensions import override
@@ -30,7 +29,7 @@ class LeadingLengthInfoType(DiagCodedType):
30
29
  @staticmethod
31
30
  @override
32
31
  def from_et(et_element: ElementTree.Element,
33
- doc_frags: List[OdxDocFragment]) -> "LeadingLengthInfoType":
32
+ doc_frags: list[OdxDocFragment]) -> "LeadingLengthInfoType":
34
33
  kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
35
34
 
36
35
  bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
@@ -53,7 +52,7 @@ class LeadingLengthInfoType(DiagCodedType):
53
52
  @override
54
53
  def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
55
54
 
56
- if not isinstance(internal_value, (str, bytes)):
55
+ if not isinstance(internal_value, str | bytes):
57
56
  odxraise(
58
57
  f"LEADING-LENGTH-INFO types can only be used for strings and byte fields, "
59
58
  f"not {type(internal_value).__name__}", EncodeError)
odxtools/library.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, cast
3
+ from typing import Any, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
@@ -19,17 +19,17 @@ class Library(IdentifiableElement):
19
19
  """
20
20
 
21
21
  code_file: str
22
- encryption: Optional[str]
22
+ encryption: str | None
23
23
  syntax: str
24
24
  revision: str
25
- entrypoint: Optional[str]
25
+ entrypoint: str | None
26
26
 
27
27
  @property
28
28
  def code(self) -> bytes:
29
29
  return self._code
30
30
 
31
31
  @staticmethod
32
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Library":
32
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Library":
33
33
 
34
34
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
35
35
 
@@ -47,7 +47,7 @@ class Library(IdentifiableElement):
47
47
  entrypoint=entrypoint,
48
48
  **kwargs)
49
49
 
50
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
50
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
51
51
  return {self.odx_id: self}
52
52
 
53
53
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -0,0 +1,62 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, Any
4
+ from xml.etree import ElementTree
5
+
6
+ from .diagnostictroublecode import DiagnosticTroubleCode
7
+ from .exceptions import odxrequire
8
+ from .nameditemlist import NamedItemList
9
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
10
+ from .snrefcontext import SnRefContext
11
+
12
+ if TYPE_CHECKING:
13
+ from .dtcdop import DtcDop
14
+
15
+
16
+ @dataclass
17
+ class LinkedDtcDop:
18
+ not_inherited_dtc_snrefs: list[str]
19
+ dtc_dop_ref: OdxLinkRef
20
+
21
+ @property
22
+ def not_inherited_dtcs(self) -> NamedItemList[DiagnosticTroubleCode]:
23
+ return self._not_inherited_dtcs
24
+
25
+ @property
26
+ def dtc_dop(self) -> "DtcDop":
27
+ return self._dtc_dop
28
+
29
+ @property
30
+ def short_name(self) -> str:
31
+ return self._dtc_dop.short_name
32
+
33
+ @staticmethod
34
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "LinkedDtcDop":
35
+ not_inherited_dtc_snrefs = [
36
+ odxrequire(el.get("SHORT-NAME"))
37
+ for el in et_element.iterfind("NOT-INHERITED-DTC-SNREFS/"
38
+ "NOT-INHERITED-DTC-SNREF")
39
+ ]
40
+
41
+ dtc_dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DTC-DOP-REF"), doc_frags))
42
+
43
+ return LinkedDtcDop(
44
+ not_inherited_dtc_snrefs=not_inherited_dtc_snrefs, dtc_dop_ref=dtc_dop_ref)
45
+
46
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
47
+ return {}
48
+
49
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
50
+ if TYPE_CHECKING:
51
+ self._dtc_dop = odxlinks.resolve(self.dtc_dop_ref, DtcDop)
52
+ else:
53
+ self._dtc_dop = odxlinks.resolve(self.dtc_dop_ref)
54
+
55
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
56
+ dtc_dop = self._dtc_dop
57
+ not_inherited_dtcs = [
58
+ resolve_snref(ni_snref, dtc_dop.dtcs, DiagnosticTroubleCode)
59
+ for ni_snref in self.not_inherited_dtc_snrefs
60
+ ]
61
+
62
+ self._not_inherited_dtcs = NamedItemList(not_inherited_dtcs)
odxtools/loadfile.py CHANGED
@@ -1,19 +1,18 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import os
3
3
  from pathlib import Path
4
- from typing import Union
5
4
 
6
5
  from .database import Database
7
6
 
8
7
 
9
- def load_pdx_file(pdx_file: Union[str, Path]) -> Database:
8
+ def load_pdx_file(pdx_file: str | Path) -> Database:
10
9
  db = Database()
11
10
  db.add_pdx_file(str(pdx_file))
12
11
  db.refresh()
13
12
  return db
14
13
 
15
14
 
16
- def load_odx_d_file(odx_d_file_name: Union[str, Path]) -> Database:
15
+ def load_odx_d_file(odx_d_file_name: str | Path) -> Database:
17
16
  db = Database()
18
17
  db.add_odx_file(str(odx_d_file_name))
19
18
  db.refresh()
@@ -21,7 +20,7 @@ def load_odx_d_file(odx_d_file_name: Union[str, Path]) -> Database:
21
20
  return db
22
21
 
23
22
 
24
- def load_file(file_name: Union[str, Path]) -> Database:
23
+ def load_file(file_name: str | Path) -> Database:
25
24
  if str(file_name).lower().endswith(".pdx"):
26
25
  return load_pdx_file(str(file_name))
27
26
  elif str(file_name).lower().endswith(".odx-d"):
@@ -30,7 +29,7 @@ def load_file(file_name: Union[str, Path]) -> Database:
30
29
  raise RuntimeError(f"Could not guess the file format of file '{file_name}'!")
31
30
 
32
31
 
33
- def load_files(*file_names: Union[str, Path]) -> Database:
32
+ def load_files(*file_names: str | Path) -> Database:
34
33
  db = Database()
35
34
  for file_name in file_names:
36
35
  p = Path(file_name)
@@ -45,7 +44,7 @@ def load_files(*file_names: Union[str, Path]) -> Database:
45
44
  return db
46
45
 
47
46
 
48
- def load_directory(dir_name: Union[str, Path]) -> Database:
47
+ def load_directory(dir_name: str | Path) -> Database:
49
48
  db = Database()
50
49
  for file_name in os.listdir(str(dir_name)):
51
50
  p = Path(dir_name) / file_name
@@ -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 .matchingparameter import MatchingParameter
@@ -18,7 +17,7 @@ class MatchingBaseVariantParameter(MatchingParameter):
18
17
  additional subtag `USE-PHYSICAL-ADDRESSING`.
19
18
  """
20
19
 
21
- use_physical_addressing_raw: Optional[bool]
20
+ use_physical_addressing_raw: bool | None
22
21
 
23
22
  @property
24
23
  def use_physical_addressing(self) -> bool:
@@ -26,7 +25,7 @@ class MatchingBaseVariantParameter(MatchingParameter):
26
25
 
27
26
  @staticmethod
28
27
  def from_et(et_element: ElementTree.Element,
29
- doc_frags: List[OdxDocFragment]) -> "MatchingBaseVariantParameter":
28
+ doc_frags: list[OdxDocFragment]) -> "MatchingBaseVariantParameter":
30
29
 
31
30
  kwargs = dataclass_fields_asdict(MatchingParameter.from_et(et_element, doc_frags))
32
31
 
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional, cast
3
+ from typing import Any, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .diaglayers.diaglayer import DiagLayer
@@ -34,12 +34,12 @@ class MatchingParameter:
34
34
  # or negative response. What it probably actually wants to say is
35
35
  # that any response that can possibly be received shall exhibit
36
36
  # the referenced parameter.
37
- out_param_if_snref: Optional[str]
38
- out_param_if_snpathref: Optional[str]
37
+ out_param_if_snref: str | None
38
+ out_param_if_snpathref: str | None
39
39
 
40
40
  @staticmethod
41
41
  def from_et(et_element: ElementTree.Element,
42
- doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
42
+ doc_frags: list[OdxDocFragment]) -> "MatchingParameter":
43
43
 
44
44
  expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
45
45
  diag_comm_snref = odxrequire(
@@ -61,7 +61,7 @@ class MatchingParameter:
61
61
  out_param_if_snpathref=out_param_if_snpathref,
62
62
  )
63
63
 
64
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
64
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
65
65
  return {}
66
66
 
67
67
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -93,7 +93,7 @@ class MatchingParameter:
93
93
 
94
94
  return self.__matches(param_dict, snpath_chunks)
95
95
 
96
- def __matches(self, param_dict: ParameterValue, snpath_chunks: List[str]) -> bool:
96
+ def __matches(self, param_dict: ParameterValue, snpath_chunks: list[str]) -> bool:
97
97
  if len(snpath_chunks) == 0:
98
98
  parameter_value = param_dict
99
99
  if isinstance(parameter_value, dict):
@@ -105,7 +105,7 @@ class MatchingParameter:
105
105
  # floating point
106
106
  return abs(float(self.expected_value) - parameter_value) < 1e-8
107
107
  elif isinstance(parameter_value, BytesTypes):
108
- return parameter_value.hex().upper() == self.expected_value.upper()
108
+ return bytes(parameter_value).hex().upper() == self.expected_value.upper()
109
109
  elif isinstance(parameter_value, DiagnosticTroubleCode):
110
110
  # TODO: what happens if non-numerical DTCs like
111
111
  # "U123456" are specified? Is specifying DTCs even
@@ -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 List, Optional, cast
3
+ from typing import cast
5
4
  from xml.etree import ElementTree
6
5
 
7
6
  from typing_extensions import override
@@ -13,18 +12,13 @@ from .encoding import get_string_encoding
13
12
  from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
14
13
  from .odxlink import OdxDocFragment
15
14
  from .odxtypes import AtomicOdxType, BytesTypes, DataType
15
+ from .termination import Termination
16
16
  from .utils import dataclass_fields_asdict
17
17
 
18
18
 
19
- class Termination(Enum):
20
- END_OF_PDU = "END-OF-PDU"
21
- ZERO = "ZERO"
22
- HEX_FF = "HEX-FF"
23
-
24
-
25
19
  @dataclass
26
20
  class MinMaxLengthType(DiagCodedType):
27
- max_length: Optional[int]
21
+ max_length: int | None
28
22
  min_length: int
29
23
  termination: Termination
30
24
 
@@ -35,7 +29,7 @@ class MinMaxLengthType(DiagCodedType):
35
29
  @staticmethod
36
30
  @override
37
31
  def from_et(et_element: ElementTree.Element,
38
- doc_frags: List[OdxDocFragment]) -> "MinMaxLengthType":
32
+ doc_frags: list[OdxDocFragment]) -> "MinMaxLengthType":
39
33
  kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
40
34
 
41
35
  max_length = None
@@ -83,7 +77,7 @@ class MinMaxLengthType(DiagCodedType):
83
77
  @override
84
78
  def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
85
79
 
86
- if not isinstance(internal_value, (str, BytesTypes)):
80
+ if not isinstance(internal_value, str | BytesTypes):
87
81
  odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
88
82
  EncodeError)
89
83
 
odxtools/modification.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 .exceptions import odxrequire
@@ -11,16 +11,16 @@ from .snrefcontext import SnRefContext
11
11
  @dataclass
12
12
  class Modification:
13
13
  change: str
14
- reason: Optional[str]
14
+ reason: str | None
15
15
 
16
16
  @staticmethod
17
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Modification":
17
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Modification":
18
18
  change = odxrequire(et_element.findtext("CHANGE"))
19
19
  reason = et_element.findtext("REASON")
20
20
 
21
21
  return Modification(change=change, reason=reason)
22
22
 
23
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
23
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
24
24
  return {}
25
25
 
26
26
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
odxtools/multiplexer.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, Tuple, Union, cast
3
+ from typing import Any, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -30,9 +30,9 @@ class Multiplexer(ComplexDop):
30
30
 
31
31
  byte_position: int
32
32
  switch_key: MultiplexerSwitchKey
33
- default_case: Optional[MultiplexerDefaultCase]
33
+ default_case: MultiplexerDefaultCase | None
34
34
  cases: NamedItemList[MultiplexerCase]
35
- is_visible_raw: Optional[bool]
35
+ is_visible_raw: bool | None
36
36
 
37
37
  @property
38
38
  def is_visible(self) -> bool:
@@ -40,7 +40,7 @@ class Multiplexer(ComplexDop):
40
40
 
41
41
  @staticmethod
42
42
  @override
43
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Multiplexer":
43
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Multiplexer":
44
44
  """Reads a Multiplexer from Diag Layer."""
45
45
  kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element, doc_frags))
46
46
 
@@ -66,7 +66,7 @@ class Multiplexer(ComplexDop):
66
66
  **kwargs)
67
67
 
68
68
  @override
69
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
69
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
70
70
  odxlinks = super()._build_odxlinks()
71
71
 
72
72
  odxlinks.update(self.switch_key._build_odxlinks())
@@ -98,7 +98,7 @@ class Multiplexer(ComplexDop):
98
98
  for mux_case in self.cases:
99
99
  mux_case._resolve_snrefs(context)
100
100
 
101
- def _get_case_limits(self, case: MultiplexerCase) -> Tuple[AtomicOdxType, AtomicOdxType]:
101
+ def _get_case_limits(self, case: MultiplexerCase) -> tuple[AtomicOdxType, AtomicOdxType]:
102
102
  key_type = self.switch_key.dop.physical_type.base_data_type
103
103
  lower_limit = key_type.make_from(case.lower_limit.value)
104
104
  upper_limit = key_type.make_from(case.upper_limit.value)
@@ -118,7 +118,7 @@ class Multiplexer(ComplexDop):
118
118
  orig_origin = encode_state.origin_byte_position
119
119
  encode_state.origin_byte_position = encode_state.cursor_byte_position
120
120
 
121
- if isinstance(physical_value, (list, tuple)) and len(physical_value) == 2:
121
+ if isinstance(physical_value, list | tuple) and len(physical_value) == 2:
122
122
  case_spec, case_value = physical_value
123
123
  elif isinstance(physical_value, dict) and len(physical_value) == 1:
124
124
  case_spec, case_value = next(iter(physical_value.items()))
@@ -127,8 +127,8 @@ class Multiplexer(ComplexDop):
127
127
  f"Values of multiplexer parameters must be defined as a "
128
128
  f"(case_name, content_value) tuple instead of as '{physical_value!r}'")
129
129
 
130
- mux_case: Union[MultiplexerCase, MultiplexerDefaultCase]
131
- applicable_cases: List[Union[MultiplexerCase, MultiplexerDefaultCase]]
130
+ mux_case: MultiplexerCase | MultiplexerDefaultCase
131
+ applicable_cases: list[MultiplexerCase | MultiplexerDefaultCase]
132
132
 
133
133
  if isinstance(case_spec, str):
134
134
  applicable_cases = [x for x in self.cases if x.short_name == case_spec]
@@ -147,7 +147,7 @@ class Multiplexer(ComplexDop):
147
147
  elif isinstance(case_spec, int):
148
148
  applicable_cases = []
149
149
  for x in self.cases:
150
- lower, upper = cast(Tuple[int, int], self._get_case_limits(x))
150
+ lower, upper = cast(tuple[int, int], self._get_case_limits(x))
151
151
  if lower <= case_spec and case_spec <= upper:
152
152
  applicable_cases.append(x)
153
153
 
@@ -208,7 +208,7 @@ class Multiplexer(ComplexDop):
208
208
  # relatively to the byte position of the MUX."
209
209
  decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
210
210
 
211
- applicable_case: Optional[Union[MultiplexerCase, MultiplexerDefaultCase]] = None
211
+ applicable_case: MultiplexerCase | MultiplexerDefaultCase | None = None
212
212
  for mux_case in self.cases:
213
213
  lower, upper = self._get_case_limits(mux_case)
214
214
  if lower <= key_value and key_value <= upper: # type: ignore[operator]
@@ -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 .compumethods.limit import Limit
@@ -17,18 +17,18 @@ from .utils import dataclass_fields_asdict
17
17
  class MultiplexerCase(NamedElement):
18
18
  """This class represents a case which represents a range of keys of a multiplexer."""
19
19
 
20
- structure_ref: Optional[OdxLinkRef]
21
- structure_snref: Optional[str]
20
+ structure_ref: OdxLinkRef | None
21
+ structure_snref: str | None
22
22
  lower_limit: Limit
23
23
  upper_limit: Limit
24
24
 
25
25
  @property
26
- def structure(self) -> Optional[Structure]:
26
+ def structure(self) -> Structure | None:
27
27
  return self._structure
28
28
 
29
29
  @staticmethod
30
30
  def from_et(et_element: ElementTree.Element,
31
- doc_frags: List[OdxDocFragment]) -> "MultiplexerCase":
31
+ doc_frags: list[OdxDocFragment]) -> "MultiplexerCase":
32
32
  """Reads a case for a Multiplexer."""
33
33
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
34
34
  structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
@@ -54,7 +54,7 @@ class MultiplexerCase(NamedElement):
54
54
  upper_limit=upper_limit,
55
55
  **kwargs)
56
56
 
57
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
57
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
58
58
  return {}
59
59
 
60
60
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -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 NamedElement
@@ -14,16 +14,16 @@ from .utils import dataclass_fields_asdict
14
14
  @dataclass
15
15
  class MultiplexerDefaultCase(NamedElement):
16
16
  """This class represents a Default Case, which is selected when there are no cases defined in the Multiplexer."""
17
- structure_ref: Optional[OdxLinkRef]
18
- structure_snref: Optional[str]
17
+ structure_ref: OdxLinkRef | None
18
+ structure_snref: str | None
19
19
 
20
20
  @property
21
- def structure(self) -> Optional[Structure]:
21
+ def structure(self) -> Structure | None:
22
22
  return self._structure
23
23
 
24
24
  @staticmethod
25
25
  def from_et(et_element: ElementTree.Element,
26
- doc_frags: List[OdxDocFragment]) -> "MultiplexerDefaultCase":
26
+ doc_frags: list[OdxDocFragment]) -> "MultiplexerDefaultCase":
27
27
  """Reads a default case for a multiplexer."""
28
28
  kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
29
29
 
@@ -35,7 +35,7 @@ class MultiplexerDefaultCase(NamedElement):
35
35
  return MultiplexerDefaultCase(
36
36
  structure_ref=structure_ref, structure_snref=structure_snref, **kwargs)
37
37
 
38
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
38
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
39
39
  return {}
40
40
 
41
41
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: