odxtools 9.7.0__py3-none-any.whl → 10.1.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 (193) hide show
  1. odxtools/additionalaudience.py +7 -7
  2. odxtools/admindata.py +14 -13
  3. odxtools/audience.py +17 -17
  4. odxtools/basecomparam.py +9 -8
  5. odxtools/basevariantpattern.py +9 -10
  6. odxtools/basicstructure.py +15 -15
  7. odxtools/cli/_print_utils.py +34 -22
  8. odxtools/cli/browse.py +8 -8
  9. odxtools/cli/compare.py +24 -24
  10. odxtools/cli/decode.py +3 -4
  11. odxtools/cli/find.py +4 -5
  12. odxtools/cli/list.py +6 -6
  13. odxtools/cli/main.py +2 -2
  14. odxtools/cli/snoop.py +3 -3
  15. odxtools/codec.py +3 -3
  16. odxtools/commrelation.py +18 -17
  17. odxtools/companydata.py +13 -13
  18. odxtools/companydocinfo.py +15 -17
  19. odxtools/companyrevisioninfo.py +9 -9
  20. odxtools/companyspecificinfo.py +11 -13
  21. odxtools/comparam.py +8 -7
  22. odxtools/comparaminstance.py +14 -14
  23. odxtools/comparamspec.py +10 -11
  24. odxtools/comparamsubset.py +17 -25
  25. odxtools/complexcomparam.py +14 -14
  26. odxtools/complexdop.py +1 -1
  27. odxtools/compositecodec.py +8 -8
  28. odxtools/compumethods/compucodecompumethod.py +7 -7
  29. odxtools/compumethods/compuconst.py +5 -6
  30. odxtools/compumethods/compudefaultvalue.py +2 -3
  31. odxtools/compumethods/compuinternaltophys.py +13 -12
  32. odxtools/compumethods/compumethod.py +10 -9
  33. odxtools/compumethods/compuphystointernal.py +13 -12
  34. odxtools/compumethods/compurationalcoeffs.py +7 -7
  35. odxtools/compumethods/compuscale.py +15 -16
  36. odxtools/compumethods/createanycompumethod.py +12 -13
  37. odxtools/compumethods/identicalcompumethod.py +4 -5
  38. odxtools/compumethods/limit.py +14 -14
  39. odxtools/compumethods/linearcompumethod.py +5 -5
  40. odxtools/compumethods/linearsegment.py +10 -11
  41. odxtools/compumethods/ratfunccompumethod.py +6 -6
  42. odxtools/compumethods/ratfuncsegment.py +7 -8
  43. odxtools/compumethods/scalelinearcompumethod.py +9 -9
  44. odxtools/compumethods/scaleratfunccompumethod.py +7 -7
  45. odxtools/compumethods/tabintpcompumethod.py +10 -13
  46. odxtools/compumethods/texttablecompumethod.py +6 -6
  47. odxtools/createanycomparam.py +5 -7
  48. odxtools/createanydiagcodedtype.py +7 -8
  49. odxtools/database.py +34 -31
  50. odxtools/dataobjectproperty.py +19 -20
  51. odxtools/decodestate.py +5 -5
  52. odxtools/description.py +9 -9
  53. odxtools/determinenumberofitems.py +8 -7
  54. odxtools/diagcodedtype.py +10 -10
  55. odxtools/diagcomm.py +29 -30
  56. odxtools/diagdatadictionaryspec.py +36 -36
  57. odxtools/diaglayercontainer.py +35 -34
  58. odxtools/diaglayers/basevariant.py +14 -12
  59. odxtools/diaglayers/basevariantraw.py +22 -23
  60. odxtools/diaglayers/diaglayer.py +24 -22
  61. odxtools/diaglayers/diaglayerraw.py +43 -52
  62. odxtools/diaglayers/diaglayertype.py +1 -2
  63. odxtools/diaglayers/ecushareddata.py +9 -9
  64. odxtools/diaglayers/ecushareddataraw.py +15 -16
  65. odxtools/diaglayers/ecuvariant.py +15 -13
  66. odxtools/diaglayers/ecuvariantraw.py +21 -22
  67. odxtools/diaglayers/functionalgroup.py +12 -11
  68. odxtools/diaglayers/functionalgroupraw.py +17 -18
  69. odxtools/diaglayers/hierarchyelement.py +48 -54
  70. odxtools/diaglayers/hierarchyelementraw.py +10 -11
  71. odxtools/diaglayers/protocol.py +7 -7
  72. odxtools/diaglayers/protocolraw.py +13 -14
  73. odxtools/diagnostictroublecode.py +15 -17
  74. odxtools/diagservice.py +28 -27
  75. odxtools/diagvariable.py +24 -25
  76. odxtools/docrevision.py +18 -17
  77. odxtools/dopbase.py +13 -14
  78. odxtools/dtcconnector.py +8 -7
  79. odxtools/dtcdop.py +24 -20
  80. odxtools/dynamicendmarkerfield.py +10 -9
  81. odxtools/dynamiclengthfield.py +10 -9
  82. odxtools/dyndefinedspec.py +10 -10
  83. odxtools/dynenddopref.py +9 -9
  84. odxtools/dyniddefmodeinfo.py +21 -21
  85. odxtools/ecuvariantpattern.py +8 -10
  86. odxtools/element.py +12 -13
  87. odxtools/encodestate.py +11 -11
  88. odxtools/encoding.py +2 -3
  89. odxtools/endofpdufield.py +9 -10
  90. odxtools/envdataconnector.py +8 -8
  91. odxtools/environmentdata.py +7 -9
  92. odxtools/environmentdatadescription.py +18 -17
  93. odxtools/exceptions.py +5 -5
  94. odxtools/externalaccessmethod.py +4 -6
  95. odxtools/externaldoc.py +6 -6
  96. odxtools/field.py +15 -15
  97. odxtools/functionalclass.py +9 -9
  98. odxtools/inputparam.py +11 -10
  99. odxtools/internalconstr.py +10 -11
  100. odxtools/isotp_state_machine.py +12 -11
  101. odxtools/leadinglengthinfotype.py +4 -6
  102. odxtools/library.py +9 -8
  103. odxtools/linkeddtcdop.py +9 -8
  104. odxtools/loadfile.py +5 -6
  105. odxtools/matchingbasevariantparameter.py +5 -6
  106. odxtools/matchingparameter.py +10 -10
  107. odxtools/message.py +1 -1
  108. odxtools/minmaxlengthtype.py +6 -7
  109. odxtools/modification.py +7 -6
  110. odxtools/multiplexer.py +54 -18
  111. odxtools/multiplexercase.py +13 -13
  112. odxtools/multiplexerdefaultcase.py +11 -10
  113. odxtools/multiplexerswitchkey.py +8 -8
  114. odxtools/nameditemlist.py +13 -13
  115. odxtools/negoutputparam.py +8 -8
  116. odxtools/obd.py +1 -2
  117. odxtools/odxcategory.py +14 -26
  118. odxtools/odxdoccontext.py +16 -0
  119. odxtools/odxlink.py +23 -25
  120. odxtools/odxtypes.py +18 -15
  121. odxtools/outputparam.py +9 -8
  122. odxtools/parameterinfo.py +1 -1
  123. odxtools/parameters/codedconstparameter.py +10 -10
  124. odxtools/parameters/createanyparameter.py +15 -16
  125. odxtools/parameters/dynamicparameter.py +5 -7
  126. odxtools/parameters/lengthkeyparameter.py +10 -10
  127. odxtools/parameters/matchingrequestparameter.py +6 -7
  128. odxtools/parameters/nrcconstparameter.py +13 -13
  129. odxtools/parameters/parameter.py +17 -18
  130. odxtools/parameters/parameterwithdop.py +13 -13
  131. odxtools/parameters/physicalconstantparameter.py +8 -7
  132. odxtools/parameters/reservedparameter.py +6 -8
  133. odxtools/parameters/systemparameter.py +5 -7
  134. odxtools/parameters/tableentryparameter.py +8 -8
  135. odxtools/parameters/tablekeyparameter.py +17 -17
  136. odxtools/parameters/tablestructparameter.py +11 -11
  137. odxtools/parameters/valueparameter.py +11 -11
  138. odxtools/paramlengthinfotype.py +10 -9
  139. odxtools/parentref.py +15 -13
  140. odxtools/physicaldimension.py +15 -15
  141. odxtools/physicaltype.py +5 -6
  142. odxtools/posresponsesuppressible.py +11 -12
  143. odxtools/preconditionstateref.py +11 -11
  144. odxtools/progcode.py +11 -10
  145. odxtools/protstack.py +10 -9
  146. odxtools/relateddiagcommref.py +5 -6
  147. odxtools/relateddoc.py +11 -10
  148. odxtools/request.py +18 -19
  149. odxtools/response.py +19 -20
  150. odxtools/scaleconstr.py +8 -9
  151. odxtools/servicebinner.py +5 -5
  152. odxtools/singleecujob.py +16 -15
  153. odxtools/snrefcontext.py +3 -3
  154. odxtools/specialdata.py +8 -7
  155. odxtools/specialdatagroup.py +17 -17
  156. odxtools/specialdatagroupcaption.py +7 -6
  157. odxtools/standardlengthtype.py +14 -22
  158. odxtools/state.py +7 -6
  159. odxtools/statechart.py +12 -11
  160. odxtools/statemachine.py +4 -3
  161. odxtools/statetransition.py +9 -9
  162. odxtools/statetransitionref.py +19 -19
  163. odxtools/staticfield.py +9 -7
  164. odxtools/structure.py +5 -6
  165. odxtools/subcomponent.py +20 -18
  166. odxtools/subcomponentparamconnector.py +10 -9
  167. odxtools/subcomponentpattern.py +9 -9
  168. odxtools/swvariable.py +6 -7
  169. odxtools/table.py +25 -26
  170. odxtools/tablediagcommconnector.py +9 -8
  171. odxtools/tablerow.py +64 -43
  172. odxtools/tablerowconnector.py +8 -8
  173. odxtools/teammember.py +16 -15
  174. odxtools/templates/macros/printParentRef.xml.jinja2 +3 -1
  175. odxtools/text.py +4 -5
  176. odxtools/uds.py +2 -3
  177. odxtools/unit.py +14 -13
  178. odxtools/unitgroup.py +11 -10
  179. odxtools/unitspec.py +18 -19
  180. odxtools/utils.py +3 -3
  181. odxtools/variablegroup.py +5 -6
  182. odxtools/variantmatcher.py +10 -10
  183. odxtools/variantpattern.py +5 -6
  184. odxtools/version.py +2 -2
  185. odxtools/writepdxfile.py +5 -24
  186. odxtools/xdoc.py +13 -12
  187. {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/METADATA +4 -5
  188. odxtools-10.1.0.dist-info/RECORD +265 -0
  189. {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/WHEEL +1 -1
  190. odxtools-9.7.0.dist-info/RECORD +0 -264
  191. {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/entry_points.txt +0 -0
  192. {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/licenses/LICENSE +0 -0
  193. {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/top_level.txt +0 -0
odxtools/request.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional, cast
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
@@ -14,7 +14,8 @@ from .element import IdentifiableElement
14
14
  from .encodestate import EncodeState
15
15
  from .exceptions import odxraise
16
16
  from .nameditemlist import NamedItemList
17
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
17
+ from .odxdoccontext import OdxDocContext
18
+ from .odxlink import OdxLinkDatabase, OdxLinkId
18
19
  from .odxtypes import ParameterValue, ParameterValueDict
19
20
  from .parameters.createanyparameter import create_any_parameter_from_et
20
21
  from .parameters.parameter import Parameter
@@ -23,40 +24,38 @@ from .specialdatagroup import SpecialDataGroup
23
24
  from .utils import dataclass_fields_asdict
24
25
 
25
26
 
26
- @dataclass
27
+ @dataclass(kw_only=True)
27
28
  class Request(IdentifiableElement):
28
29
  """Represents all information related to an UDS request
29
30
 
30
31
  This class implements the `CompositeCodec` interface.
31
32
  """
32
- admin_data: Optional[AdminData]
33
- parameters: NamedItemList[Parameter]
34
- sdgs: List[SpecialDataGroup]
33
+ admin_data: AdminData | None = None
34
+ parameters: NamedItemList[Parameter] = field(default_factory=NamedItemList)
35
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
35
36
 
36
37
  @property
37
- def required_parameters(self) -> List[Parameter]:
38
+ def required_parameters(self) -> list[Parameter]:
38
39
  return composite_codec_get_required_parameters(self)
39
40
 
40
41
  @property
41
- def free_parameters(self) -> List[Parameter]:
42
+ def free_parameters(self) -> list[Parameter]:
42
43
  return composite_codec_get_free_parameters(self)
43
44
 
44
45
  @staticmethod
45
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Request":
46
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
46
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Request":
47
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
47
48
 
48
- admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
49
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
49
50
  parameters = NamedItemList([
50
- create_any_parameter_from_et(et_parameter, doc_frags)
51
+ create_any_parameter_from_et(et_parameter, context)
51
52
  for et_parameter in et_element.iterfind("PARAMS/PARAM")
52
53
  ])
53
- sdgs = [
54
- SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
55
- ]
54
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
56
55
 
57
56
  return Request(admin_data=admin_data, parameters=parameters, sdgs=sdgs, **kwargs)
58
57
 
59
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
58
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
60
59
  result = {self.odx_id: self}
61
60
 
62
61
  if self.admin_data is not None:
@@ -96,7 +95,7 @@ class Request(IdentifiableElement):
96
95
  context.request = None
97
96
  context.parameters = None
98
97
 
99
- def get_static_bit_length(self) -> Optional[int]:
98
+ def get_static_bit_length(self) -> int | None:
100
99
  return composite_codec_get_static_bit_length(self)
101
100
 
102
101
  def print_free_parameters_info(self) -> None:
@@ -123,7 +122,7 @@ class Request(IdentifiableElement):
123
122
 
124
123
  return cast(ParameterValueDict, param_values)
125
124
 
126
- def encode_into_pdu(self, physical_value: Optional[ParameterValue],
125
+ def encode_into_pdu(self, physical_value: ParameterValue | None,
127
126
  encode_state: EncodeState) -> None:
128
127
  composite_codec_encode_into_pdu(self, physical_value, encode_state)
129
128
 
odxtools/response.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass
2
+ from dataclasses import dataclass, field
3
3
  from enum import Enum
4
- from typing import Any, Dict, List, Optional, cast
4
+ from typing import Any, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .admindata import AdminData
@@ -15,7 +15,8 @@ from .element import IdentifiableElement
15
15
  from .encodestate import EncodeState
16
16
  from .exceptions import odxraise
17
17
  from .nameditemlist import NamedItemList
18
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
18
+ from .odxdoccontext import OdxDocContext
19
+ from .odxlink import OdxLinkDatabase, OdxLinkId
19
20
  from .odxtypes import ParameterValue, ParameterValueDict
20
21
  from .parameters.createanyparameter import create_any_parameter_from_et
21
22
  from .parameters.parameter import Parameter
@@ -30,7 +31,7 @@ class ResponseType(Enum):
30
31
  GLOBAL_NEGATIVE = "GLOBAL-NEG-RESPONSE"
31
32
 
32
33
 
33
- @dataclass
34
+ @dataclass(kw_only=True)
34
35
  class Response(IdentifiableElement):
35
36
  """Represents all information related to an UDS response
36
37
 
@@ -39,14 +40,14 @@ class Response(IdentifiableElement):
39
40
 
40
41
  response_type: ResponseType
41
42
 
42
- admin_data: Optional[AdminData]
43
- parameters: NamedItemList[Parameter]
44
- sdgs: List[SpecialDataGroup]
43
+ admin_data: AdminData | None = None
44
+ parameters: NamedItemList[Parameter] = field(default_factory=NamedItemList)
45
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
45
46
 
46
47
  @staticmethod
47
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Response":
48
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Response":
48
49
  """Reads a response."""
49
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
50
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
50
51
 
51
52
  try:
52
53
  response_type = ResponseType(et_element.tag)
@@ -54,14 +55,12 @@ class Response(IdentifiableElement):
54
55
  response_type = cast(ResponseType, None)
55
56
  odxraise(f"Encountered unknown response type '{et_element.tag}'")
56
57
 
57
- admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
58
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
58
59
  parameters = NamedItemList([
59
- create_any_parameter_from_et(et_parameter, doc_frags)
60
+ create_any_parameter_from_et(et_parameter, context)
60
61
  for et_parameter in et_element.iterfind("PARAMS/PARAM")
61
62
  ])
62
- sdgs = [
63
- SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
64
- ]
63
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
65
64
 
66
65
  return Response(
67
66
  response_type=response_type,
@@ -70,7 +69,7 @@ class Response(IdentifiableElement):
70
69
  sdgs=sdgs,
71
70
  **kwargs)
72
71
 
73
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
72
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
74
73
  result = {self.odx_id: self}
75
74
 
76
75
  if self.admin_data is not None:
@@ -110,7 +109,7 @@ class Response(IdentifiableElement):
110
109
  context.response = None
111
110
  context.parameters = None
112
111
 
113
- def encode(self, coded_request: Optional[bytes] = None, **kwargs: ParameterValue) -> bytearray:
112
+ def encode(self, coded_request: bytes | None = None, **kwargs: ParameterValue) -> bytearray:
114
113
  encode_state = EncodeState(triggering_request=coded_request, is_end_of_pdu=True)
115
114
 
116
115
  self.encode_into_pdu(physical_value=kwargs, encode_state=encode_state)
@@ -126,22 +125,22 @@ class Response(IdentifiableElement):
126
125
 
127
126
  return cast(ParameterValueDict, param_values)
128
127
 
129
- def encode_into_pdu(self, physical_value: Optional[ParameterValue],
128
+ def encode_into_pdu(self, physical_value: ParameterValue | None,
130
129
  encode_state: EncodeState) -> None:
131
130
  composite_codec_encode_into_pdu(self, physical_value, encode_state)
132
131
 
133
132
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
134
133
  return composite_codec_decode_from_pdu(self, decode_state)
135
134
 
136
- def get_static_bit_length(self) -> Optional[int]:
135
+ def get_static_bit_length(self) -> int | None:
137
136
  return composite_codec_get_static_bit_length(self)
138
137
 
139
138
  @property
140
- def required_parameters(self) -> List[Parameter]:
139
+ def required_parameters(self) -> list[Parameter]:
141
140
  return composite_codec_get_required_parameters(self)
142
141
 
143
142
  @property
144
- def free_parameters(self) -> List[Parameter]:
143
+ def free_parameters(self) -> list[Parameter]:
145
144
  return composite_codec_get_free_parameters(self)
146
145
 
147
146
  def print_free_parameters_info(self) -> None:
odxtools/scaleconstr.py CHANGED
@@ -1,37 +1,36 @@
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
7
6
  from .description import Description
8
7
  from .exceptions import odxraise, odxrequire
9
- from .odxlink import OdxDocFragment
8
+ from .odxdoccontext import OdxDocContext
10
9
  from .odxtypes import DataType
11
10
  from .validtype import ValidType
12
11
 
13
12
 
14
- @dataclass
13
+ @dataclass(kw_only=True)
15
14
  class ScaleConstr:
16
15
  """This class represents a SCALE-CONSTR.
17
16
  """
18
17
 
19
- short_label: Optional[str]
20
- description: Optional[Description]
18
+ short_label: str | None = None
19
+ description: Description | None = None
21
20
  lower_limit: Limit
22
21
  upper_limit: Limit
23
22
  validity: ValidType
24
23
  value_type: DataType
25
24
 
26
25
  @staticmethod
27
- def scale_constr_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
26
+ def scale_constr_from_et(et_element: ElementTree.Element, context: OdxDocContext, *,
28
27
  value_type: DataType) -> "ScaleConstr":
29
28
  short_label = et_element.findtext("SHORT-LABEL")
30
- description = Description.from_et(et_element.find("DESC"), doc_frags)
29
+ description = Description.from_et(et_element.find("DESC"), context)
31
30
  lower_limit = Limit.limit_from_et(
32
- odxrequire(et_element.find("LOWER-LIMIT")), doc_frags, value_type=value_type)
31
+ odxrequire(et_element.find("LOWER-LIMIT")), context, value_type=value_type)
33
32
  upper_limit = Limit.limit_from_et(
34
- odxrequire(et_element.find("UPPER-LIMIT")), doc_frags, value_type=value_type)
33
+ odxrequire(et_element.find("UPPER-LIMIT")), context, value_type=value_type)
35
34
 
36
35
  validity_str = odxrequire(et_element.get("VALIDITY"))
37
36
  try:
odxtools/servicebinner.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
+ from collections.abc import Iterable, Iterator
2
3
  from io import StringIO
3
- from typing import Dict, Iterable, Iterator, Optional
4
4
 
5
5
  from . import obd, uds
6
6
  from .diagservice import DiagService
@@ -21,7 +21,7 @@ class ServiceBinner:
21
21
  """
22
22
 
23
23
  def __init__(self, services: Iterable[DiagService]):
24
- service_groups: Dict[Optional[int], NamedItemList[DiagService]] = {}
24
+ service_groups: dict[int | None, NamedItemList[DiagService]] = {}
25
25
  for service in services:
26
26
  SID = self.__extract_sid(service)
27
27
 
@@ -32,7 +32,7 @@ class ServiceBinner:
32
32
 
33
33
  self._service_groups = service_groups
34
34
 
35
- def __extract_sid(self, service: DiagService) -> Optional[int]:
35
+ def __extract_sid(self, service: DiagService) -> int | None:
36
36
  # diagnostic services without requests are possible; just like
37
37
  # aircraft without wings...
38
38
  if service.request is None:
@@ -96,10 +96,10 @@ class ServiceBinner:
96
96
 
97
97
  return result.getvalue()
98
98
 
99
- def __iter__(self) -> Iterator[Optional[int]]:
99
+ def __iter__(self) -> Iterator[int | None]:
100
100
  return iter(self._service_groups)
101
101
 
102
- def __getitem__(self, sid: Optional[int]) -> NamedItemList[DiagService]:
102
+ def __getitem__(self, sid: int | None) -> NamedItemList[DiagService]:
103
103
  if sid is None:
104
104
  return self._service_groups.get(sid, NamedItemList())
105
105
 
odxtools/singleecujob.py CHANGED
@@ -1,20 +1,21 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass
3
- from typing import Any, Dict, List
2
+ from dataclasses import dataclass, field
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .diagcomm import DiagComm
7
7
  from .inputparam import InputParam
8
8
  from .nameditemlist import NamedItemList
9
9
  from .negoutputparam import NegOutputParam
10
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
+ from .odxdoccontext import OdxDocContext
11
+ from .odxlink import OdxLinkDatabase, OdxLinkId
11
12
  from .outputparam import OutputParam
12
13
  from .progcode import ProgCode
13
14
  from .snrefcontext import SnRefContext
14
15
  from .utils import dataclass_fields_asdict
15
16
 
16
17
 
17
- @dataclass
18
+ @dataclass(kw_only=True)
18
19
  class SingleEcuJob(DiagComm):
19
20
  """A single ECU job is a diagnostic communication primitive.
20
21
 
@@ -30,30 +31,30 @@ class SingleEcuJob(DiagComm):
30
31
  standard.
31
32
  """
32
33
 
33
- prog_codes: List[ProgCode]
34
- input_params: NamedItemList[InputParam]
35
- output_params: NamedItemList[OutputParam]
36
- neg_output_params: NamedItemList[NegOutputParam]
34
+ prog_codes: list[ProgCode] = field(default_factory=list)
35
+ input_params: NamedItemList[InputParam] = field(default_factory=NamedItemList)
36
+ output_params: NamedItemList[OutputParam] = field(default_factory=NamedItemList)
37
+ neg_output_params: NamedItemList[NegOutputParam] = field(default_factory=NamedItemList)
37
38
 
38
39
  @staticmethod
39
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SingleEcuJob":
40
- kwargs = dataclass_fields_asdict(DiagComm.from_et(et_element, doc_frags))
40
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SingleEcuJob":
41
+ kwargs = dataclass_fields_asdict(DiagComm.from_et(et_element, context))
41
42
 
42
43
  prog_codes = [
43
- ProgCode.from_et(pc_elem, doc_frags)
44
+ ProgCode.from_et(pc_elem, context)
44
45
  for pc_elem in et_element.iterfind("PROG-CODES/PROG-CODE")
45
46
  ]
46
47
 
47
48
  input_params = NamedItemList([
48
- InputParam.from_et(el, doc_frags)
49
+ InputParam.from_et(el, context)
49
50
  for el in et_element.iterfind("INPUT-PARAMS/INPUT-PARAM")
50
51
  ])
51
52
  output_params = NamedItemList([
52
- OutputParam.from_et(el, doc_frags)
53
+ OutputParam.from_et(el, context)
53
54
  for el in et_element.iterfind("OUTPUT-PARAMS/OUTPUT-PARAM")
54
55
  ])
55
56
  neg_output_params = NamedItemList([
56
- NegOutputParam.from_et(el, doc_frags)
57
+ NegOutputParam.from_et(el, context)
57
58
  for el in et_element.iterfind("NEG-OUTPUT-PARAMS/NEG-OUTPUT-PARAM")
58
59
  ])
59
60
 
@@ -64,7 +65,7 @@ class SingleEcuJob(DiagComm):
64
65
  neg_output_params=neg_output_params,
65
66
  **kwargs)
66
67
 
67
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
68
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
68
69
  result = super()._build_odxlinks()
69
70
 
70
71
  for prog_code in self.prog_codes:
odxtools/snrefcontext.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, List, Optional
3
+ from typing import TYPE_CHECKING, Optional
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from .database import Database
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
  from .statechart import StateChart
14
14
 
15
15
 
16
- @dataclass
16
+ @dataclass(kw_only=True)
17
17
  class SnRefContext:
18
18
  """Represents the context for which a short name reference ought
19
19
  to be resolved
@@ -25,5 +25,5 @@ class SnRefContext:
25
25
  single_ecu_job: Optional["SingleEcuJob"] = None
26
26
  request: Optional["Request"] = None
27
27
  response: Optional["Response"] = None
28
- parameters: Optional[List["Parameter"]] = None
28
+ parameters: list["Parameter"] | None = None
29
29
  state_chart: Optional["StateChart"] = None
odxtools/specialdata.py CHANGED
@@ -1,21 +1,22 @@
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
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
6
+ from .odxdoccontext import OdxDocContext
7
+ from .odxlink import OdxLinkDatabase, OdxLinkId
7
8
  from .snrefcontext import SnRefContext
8
9
 
9
10
 
10
- @dataclass
11
+ @dataclass(kw_only=True)
11
12
  class SpecialData:
12
13
  """This corresponds to the SD XML tag"""
13
- semantic_info: Optional[str] # the "SI" attribute
14
- text_identifier: Optional[str] # the "TI" attribute, specifies the language used
14
+ semantic_info: str | None = None # the "SI" attribute
15
+ text_identifier: str | None = None # the "TI" attribute, specifies the language used
15
16
  value: str
16
17
 
17
18
  @staticmethod
18
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SpecialData":
19
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SpecialData":
19
20
  semantic_info = et_element.get("SI")
20
21
  text_identifier = et_element.get("TI")
21
22
  value = et_element.text or ""
@@ -23,7 +24,7 @@ class SpecialData:
23
24
  return SpecialData(
24
25
  semantic_info=semantic_info, text_identifier=text_identifier, value=value)
25
26
 
26
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
27
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
27
28
  return {}
28
29
 
29
30
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -1,43 +1,43 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional, Union
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, Union
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
6
+ from .odxdoccontext import OdxDocContext
7
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
7
8
  from .snrefcontext import SnRefContext
8
9
  from .specialdata import SpecialData
9
10
  from .specialdatagroupcaption import SpecialDataGroupCaption
10
11
 
11
12
 
12
- @dataclass
13
+ @dataclass(kw_only=True)
13
14
  class SpecialDataGroup:
14
15
  """This corresponds to the SDG XML tag"""
15
- sdg_caption: Optional[SpecialDataGroupCaption]
16
- sdg_caption_ref: Optional[OdxLinkRef]
17
- values: List[Union["SpecialDataGroup", SpecialData]]
18
- semantic_info: Optional[str] # the "SI" attribute
16
+ sdg_caption: SpecialDataGroupCaption | None = None
17
+ sdg_caption_ref: OdxLinkRef | None = None
18
+ values: list[Union["SpecialDataGroup", SpecialData]] = field(default_factory=list)
19
+ semantic_info: str | None = None # the "SI" attribute
19
20
 
20
21
  @staticmethod
21
- def from_et(et_element: ElementTree.Element,
22
- doc_frags: List[OdxDocFragment]) -> "SpecialDataGroup":
22
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SpecialDataGroup":
23
23
 
24
24
  sdg_caption = None
25
25
  if caption_elem := et_element.find("SDG-CAPTION"):
26
- sdg_caption = SpecialDataGroupCaption.from_et(caption_elem, doc_frags)
26
+ sdg_caption = SpecialDataGroupCaption.from_et(caption_elem, context)
27
27
 
28
28
  sdg_caption_ref = None
29
29
  if (caption_ref_elem := et_element.find("SDG-CAPTION-REF")) is not None:
30
- sdg_caption_ref = OdxLinkRef.from_et(caption_ref_elem, doc_frags)
30
+ sdg_caption_ref = OdxLinkRef.from_et(caption_ref_elem, context)
31
31
 
32
32
  semantic_info = et_element.get("SI")
33
33
 
34
- values: List[Union[SpecialData, SpecialDataGroup]] = []
34
+ values: list[SpecialData | SpecialDataGroup] = []
35
35
  for value_elem in et_element:
36
- next_entry: Optional[Union[SpecialData, SpecialDataGroup]] = None
36
+ next_entry: SpecialData | SpecialDataGroup | None = None
37
37
  if value_elem.tag == "SDG":
38
- next_entry = SpecialDataGroup.from_et(value_elem, doc_frags)
38
+ next_entry = SpecialDataGroup.from_et(value_elem, context)
39
39
  elif value_elem.tag == "SD":
40
- next_entry = SpecialData.from_et(value_elem, doc_frags)
40
+ next_entry = SpecialData.from_et(value_elem, context)
41
41
 
42
42
  if next_entry is not None:
43
43
  values.append(next_entry)
@@ -49,7 +49,7 @@ class SpecialDataGroup:
49
49
  values=values,
50
50
  )
51
51
 
52
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
52
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
53
53
  result = {}
54
54
 
55
55
  if self.sdg_caption_ref is None and self.sdg_caption is not None:
@@ -1,25 +1,26 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
7
+ from .odxdoccontext import OdxDocContext
8
+ from .odxlink import OdxLinkDatabase, OdxLinkId
8
9
  from .snrefcontext import SnRefContext
9
10
  from .utils import dataclass_fields_asdict
10
11
 
11
12
 
12
- @dataclass
13
+ @dataclass(kw_only=True)
13
14
  class SpecialDataGroupCaption(IdentifiableElement):
14
15
 
15
16
  @staticmethod
16
17
  def from_et(et_element: ElementTree.Element,
17
- doc_frags: List[OdxDocFragment]) -> "SpecialDataGroupCaption":
18
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
18
+ context: OdxDocContext) -> "SpecialDataGroupCaption":
19
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
19
20
 
20
21
  return SpecialDataGroupCaption(**kwargs)
21
22
 
22
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
23
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
23
24
  result = {self.odx_id: self}
24
25
 
25
26
  result[self.odx_id] = self
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List, Literal, Optional
3
+ from typing import Literal
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -9,17 +9,17 @@ from .decodestate import DecodeState
9
9
  from .diagcodedtype import DctType, DiagCodedType
10
10
  from .encodestate import EncodeState
11
11
  from .exceptions import odxassert, odxraise, odxrequire
12
- from .odxlink import OdxDocFragment
12
+ from .odxdoccontext import OdxDocContext
13
13
  from .odxtypes import AtomicOdxType, BytesTypes, DataType, odxstr_to_bool
14
14
  from .utils import dataclass_fields_asdict, read_hex_binary
15
15
 
16
16
 
17
- @dataclass
17
+ @dataclass(kw_only=True)
18
18
  class StandardLengthType(DiagCodedType):
19
19
 
20
20
  bit_length: int
21
- bit_mask: Optional[int]
22
- is_condensed_raw: Optional[bool]
21
+ bit_mask: int | None = None
22
+ is_condensed_raw: bool | None = None
23
23
 
24
24
  @property
25
25
  def dct_type(self) -> DctType:
@@ -31,9 +31,8 @@ class StandardLengthType(DiagCodedType):
31
31
 
32
32
  @staticmethod
33
33
  @override
34
- def from_et(et_element: ElementTree.Element,
35
- doc_frags: List[OdxDocFragment]) -> "StandardLengthType":
36
- kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
34
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "StandardLengthType":
35
+ kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, context))
37
36
 
38
37
  bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
39
38
  bit_mask = read_hex_binary(et_element.find("BIT-MASK"))
@@ -50,7 +49,7 @@ class StandardLengthType(DiagCodedType):
50
49
  'Can not apply a bit_mask on a value of type {self.base_data_type}',
51
50
  )
52
51
 
53
- def __get_used_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
52
+ def __get_used_mask(self, internal_value: AtomicOdxType) -> bytes | None:
54
53
  """Returns a byte field where all bits that are used by the
55
54
  DiagCoded type are set and all unused ones are not set.
56
55
 
@@ -72,11 +71,7 @@ class StandardLengthType(DiagCodedType):
72
71
  if self.is_condensed:
73
72
  # if a condensed bitmask is specified, the number of bits
74
73
  # set to one in the bit mask are used in the PDU
75
-
76
- # TODO: this is pretty slow. replace it by
77
- # `self.bit_mask.bit_count()` once we require python >=
78
- # 3.10.
79
- bit_sz = bin(self.bit_mask).count("1")
74
+ bit_sz = self.bit_mask.bit_count()
80
75
  used_mask = (1 << bit_sz) - 1
81
76
 
82
77
  return used_mask.to_bytes((bit_sz + 7) // 8, endianness)
@@ -118,7 +113,7 @@ class StandardLengthType(DiagCodedType):
118
113
  mask_bit += 1
119
114
 
120
115
  if isinstance(internal_value, BytesTypes):
121
- return result.to_bytes(len(internal_value), 'big')
116
+ return result.to_bytes(len(bytes(internal_value)), 'big')
122
117
 
123
118
  return result
124
119
 
@@ -156,7 +151,7 @@ class StandardLengthType(DiagCodedType):
156
151
  mask_bit += 1
157
152
 
158
153
  if isinstance(raw_value, BytesTypes):
159
- return result.to_bytes(len(raw_value), 'big')
154
+ return result.to_bytes(len(bytes(raw_value)), 'big')
160
155
 
161
156
  return result
162
157
  if isinstance(raw_value, int):
@@ -164,17 +159,14 @@ class StandardLengthType(DiagCodedType):
164
159
  if isinstance(raw_value, BytesTypes):
165
160
  int_value = int.from_bytes(raw_value, 'big')
166
161
  int_value &= self.bit_mask
167
- return int_value.to_bytes(len(raw_value), 'big')
162
+ return int_value.to_bytes(len(bytes(raw_value)), 'big')
168
163
 
169
164
  odxraise(f'Can not apply a bit_mask on a value of type {type(raw_value)}')
170
165
  return raw_value
171
166
 
172
- def get_static_bit_length(self) -> Optional[int]:
167
+ def get_static_bit_length(self) -> int | None:
173
168
  if self.bit_mask is not None and self.is_condensed:
174
- # TODO: this is pretty slow. replace it by
175
- # `self.bit_mask.bit_count()` once we require python >=
176
- # 3.10.
177
- return bin(self.bit_mask).count("1")
169
+ return self.bit_mask.bit_count()
178
170
 
179
171
  return self.bit_length
180
172
 
odxtools/state.py CHANGED
@@ -1,27 +1,28 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
7
+ from .odxdoccontext import OdxDocContext
8
+ from .odxlink import OdxLinkDatabase, OdxLinkId
8
9
  from .snrefcontext import SnRefContext
9
10
  from .utils import dataclass_fields_asdict
10
11
 
11
12
 
12
- @dataclass
13
+ @dataclass(kw_only=True)
13
14
  class State(IdentifiableElement):
14
15
  """
15
16
  Corresponds to STATE.
16
17
  """
17
18
 
18
19
  @staticmethod
19
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "State":
20
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
20
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "State":
21
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
21
22
 
22
23
  return State(**kwargs)
23
24
 
24
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
25
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
25
26
  return {self.odx_id: self}
26
27
 
27
28
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: