odxtools 5.3.1__py3-none-any.whl → 6.0.1__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 (88) hide show
  1. odxtools/__init__.py +1 -1
  2. odxtools/basicstructure.py +76 -91
  3. odxtools/cli/_parser_utils.py +12 -9
  4. odxtools/cli/_print_utils.py +7 -7
  5. odxtools/cli/browse.py +94 -73
  6. odxtools/cli/find.py +42 -59
  7. odxtools/cli/list.py +21 -17
  8. odxtools/cli/snoop.py +19 -18
  9. odxtools/communicationparameterref.py +6 -3
  10. odxtools/companydocinfo.py +2 -2
  11. odxtools/companyrevisioninfo.py +1 -1
  12. odxtools/comparamsubset.py +6 -6
  13. odxtools/complexcomparam.py +1 -1
  14. odxtools/compumethods/compumethod.py +6 -9
  15. odxtools/compumethods/createanycompumethod.py +11 -9
  16. odxtools/compumethods/identicalcompumethod.py +5 -4
  17. odxtools/compumethods/limit.py +9 -9
  18. odxtools/compumethods/linearcompumethod.py +25 -17
  19. odxtools/compumethods/scalelinearcompumethod.py +6 -5
  20. odxtools/compumethods/tabintpcompumethod.py +30 -9
  21. odxtools/compumethods/texttablecompumethod.py +22 -24
  22. odxtools/database.py +5 -5
  23. odxtools/dataobjectproperty.py +10 -23
  24. odxtools/decodestate.py +1 -1
  25. odxtools/determinenumberofitems.py +37 -8
  26. odxtools/diagcodedtype.py +14 -9
  27. odxtools/diagdatadictionaryspec.py +60 -37
  28. odxtools/diaglayer.py +30 -21
  29. odxtools/diaglayercontainer.py +40 -40
  30. odxtools/diaglayerraw.py +92 -63
  31. odxtools/diagnostictroublecode.py +2 -2
  32. odxtools/diagservice.py +53 -35
  33. odxtools/docrevision.py +1 -1
  34. odxtools/dopbase.py +14 -3
  35. odxtools/dtcdop.py +15 -9
  36. odxtools/dynamiclengthfield.py +6 -4
  37. odxtools/endofpdufield.py +22 -23
  38. odxtools/environmentdata.py +2 -5
  39. odxtools/environmentdatadescription.py +6 -4
  40. odxtools/field.py +3 -8
  41. odxtools/isotp_state_machine.py +52 -38
  42. odxtools/leadinglengthinfotype.py +9 -7
  43. odxtools/load_file.py +2 -1
  44. odxtools/load_odx_d_file.py +2 -5
  45. odxtools/load_pdx_file.py +2 -6
  46. odxtools/message.py +11 -3
  47. odxtools/minmaxlengthtype.py +107 -78
  48. odxtools/modification.py +2 -2
  49. odxtools/multiplexer.py +23 -21
  50. odxtools/multiplexerswitchkey.py +37 -8
  51. odxtools/nameditemlist.py +59 -58
  52. odxtools/odxlink.py +4 -2
  53. odxtools/odxtypes.py +4 -3
  54. odxtools/parameterinfo.py +6 -6
  55. odxtools/parameters/codedconstparameter.py +15 -25
  56. odxtools/parameters/createanyparameter.py +1 -1
  57. odxtools/parameters/dynamicparameter.py +6 -5
  58. odxtools/parameters/lengthkeyparameter.py +2 -1
  59. odxtools/parameters/matchingrequestparameter.py +8 -11
  60. odxtools/parameters/nrcconstparameter.py +11 -21
  61. odxtools/parameters/parameter.py +4 -18
  62. odxtools/parameters/parameterwithdop.py +14 -29
  63. odxtools/parameters/physicalconstantparameter.py +7 -9
  64. odxtools/parameters/reservedparameter.py +17 -38
  65. odxtools/parameters/systemparameter.py +6 -5
  66. odxtools/parameters/tableentryparameter.py +6 -5
  67. odxtools/parameters/tablekeyparameter.py +8 -15
  68. odxtools/parameters/tablestructparameter.py +11 -12
  69. odxtools/parameters/valueparameter.py +9 -24
  70. odxtools/paramlengthinfotype.py +11 -9
  71. odxtools/physicaldimension.py +1 -1
  72. odxtools/physicaltype.py +2 -2
  73. odxtools/response.py +7 -3
  74. odxtools/singleecujob.py +48 -22
  75. odxtools/standardlengthtype.py +11 -6
  76. odxtools/uds.py +1 -1
  77. odxtools/unit.py +5 -5
  78. odxtools/unitgroup.py +1 -1
  79. odxtools/unitspec.py +2 -2
  80. odxtools/version.py +13 -3
  81. odxtools/write_pdx_file.py +7 -4
  82. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
  83. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
  84. odxtools/positioneddataobjectproperty.py +0 -74
  85. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
  86. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
  87. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
  88. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,7 @@ from ..decodestate import DecodeState
6
6
  from ..encodestate import EncodeState
7
7
  from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
8
8
  from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
+ from ..odxtypes import ParameterValue
9
10
  from .parameter import Parameter, ParameterType
10
11
 
11
12
  if TYPE_CHECKING:
@@ -94,14 +95,6 @@ class TableKeyParameter(Parameter):
94
95
  def is_settable(self) -> bool:
95
96
  return True
96
97
 
97
- def get_coded_value(self, physical_value=None) -> Any:
98
- key_dop = self.table.key_dop
99
- if key_dop is None:
100
- raise EncodeError(f"Table '{self.table.short_name}' does not define "
101
- f"a KEY-DOP, but is used in TABLE-KEY parameter "
102
- f"'{self.short_name}'")
103
- return key_dop.convert_physical_to_internal(physical_value)
104
-
105
98
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
106
99
  tr_short_name = encode_state.parameter_values.get(self.short_name)
107
100
 
@@ -116,7 +109,7 @@ class TableKeyParameter(Parameter):
116
109
  f"a KEY-DOP, but is used in TABLE-KEY parameter "
117
110
  f"'{self.short_name}'")
118
111
 
119
- byte_len = (key_dop.bit_length + 7) // 8
112
+ byte_len = (odxrequire(key_dop.get_static_bit_length()) + 7) // 8
120
113
  if self.bit_position is not None and self.bit_position > 0:
121
114
  byte_len += 1
122
115
 
@@ -142,21 +135,21 @@ class TableKeyParameter(Parameter):
142
135
  def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
143
136
  return super().encode_into_pdu(encode_state)
144
137
 
145
- def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[Any, int]:
146
- if self.byte_position is not None and self.byte_position != decode_state.next_byte_position:
147
- next_byte_position = self.byte_position
138
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
139
+ if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
140
+ cursor_position = self.byte_position
148
141
 
149
142
  # update the decode_state's table key
150
143
  if self.table_row is not None:
151
144
  # the table row to be used is statically specified -> no
152
145
  # need to decode anything!
153
146
  phys_val = self.table_row.short_name
154
- next_byte_position = decode_state.next_byte_position
147
+ cursor_position = decode_state.cursor_position
155
148
  else:
156
149
  # Use DOP to decode
157
150
  key_dop = odxrequire(self.table.key_dop)
158
151
  bit_position_int = self.bit_position if self.bit_position is not None else 0
159
- key_dop_val, next_byte_position = key_dop.convert_bytes_to_physical(
152
+ key_dop_val, cursor_position = key_dop.convert_bytes_to_physical(
160
153
  decode_state, bit_position=bit_position_int)
161
154
 
162
155
  table_row_candidates = [x for x in self.table.table_rows if x.key == key_dop_val]
@@ -167,4 +160,4 @@ class TableKeyParameter(Parameter):
167
160
  f"Multiple rows exhibiting key '{str(key_dop_val)}' found in table")
168
161
  phys_val = table_row_candidates[0].short_name
169
162
 
170
- return phys_val, next_byte_position
163
+ return phys_val, cursor_position
@@ -2,12 +2,13 @@
2
2
  import warnings
3
3
  from copy import copy
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
5
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, cast
6
6
 
7
7
  from ..decodestate import DecodeState
8
8
  from ..encodestate import EncodeState
9
9
  from ..exceptions import EncodeError, OdxWarning, odxraise
10
10
  from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..odxtypes import ParameterValue
11
12
  from .parameter import Parameter, ParameterType
12
13
  from .tablekeyparameter import TableKeyParameter
13
14
 
@@ -44,24 +45,22 @@ class TableStructParameter(Parameter):
44
45
  if self.table_key_snref is not None:
45
46
  warnings.warn(
46
47
  "Table keys cannot yet be defined using SNREFs"
47
- " in TableStructParameters.", OdxWarning)
48
+ " in TableStructParameters.",
49
+ OdxWarning,
50
+ stacklevel=1)
48
51
 
49
52
  @property
50
53
  def table_key(self) -> TableKeyParameter:
51
54
  return self._table_key
52
55
 
53
56
  @property
54
- def is_required(self):
57
+ def is_required(self) -> bool:
55
58
  return True
56
59
 
57
60
  @property
58
- def is_settable(self):
61
+ def is_settable(self) -> bool:
59
62
  return True
60
63
 
61
- def get_coded_value(self, physical_value=None):
62
- raise EncodeError("TableStructParameters cannot be converted to "
63
- "internal values without a table row.")
64
-
65
64
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
66
65
  physical_value = encode_state.parameter_values.get(self.short_name)
67
66
 
@@ -131,11 +130,11 @@ class TableStructParameter(Parameter):
131
130
  def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
132
131
  return super().encode_into_pdu(encode_state)
133
132
 
134
- def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[Any, int]:
135
- if self.byte_position is not None and self.byte_position != decode_state.next_byte_position:
133
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
134
+ if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
136
135
  next_pos = self.byte_position if self.byte_position is not None else 0
137
136
  decode_state = copy(decode_state)
138
- decode_state.next_byte_position = next_pos
137
+ decode_state.cursor_position = next_pos
139
138
 
140
139
  # find the selected table row
141
140
  key_name = self.table_key.short_name
@@ -160,4 +159,4 @@ class TableStructParameter(Parameter):
160
159
  else:
161
160
  # the table row associated with the key neither defines a
162
161
  # DOP not a structure -> ignore it
163
- return (table_row.short_name, None), decode_state.next_byte_position
162
+ return (table_row.short_name, cast(int, None)), decode_state.cursor_position
@@ -31,17 +31,15 @@ class ValueParameter(ParameterWithDOP):
31
31
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
32
32
  super()._resolve_snrefs(diag_layer)
33
33
 
34
- self._physical_default_value: Optional[AtomicOdxType]
35
- pdvr = self.physical_default_value_raw
36
- if pdvr is None:
37
- self._physical_default_value = None
38
- return
39
-
40
- dop = odxrequire(self.dop)
41
- if not isinstance(dop, DataObjectProperty):
42
- odxraise("The type of PHYS-CONST parameters must be a simple DOP")
43
- base_data_type = dop.physical_type.base_data_type
44
- self._physical_default_value = base_data_type.from_string(pdvr)
34
+ self._physical_default_value: Optional[AtomicOdxType] = None
35
+ if self.physical_default_value_raw is not None:
36
+ dop = odxrequire(self.dop)
37
+ if not isinstance(dop, DataObjectProperty):
38
+ odxraise("Value parameters can only define a physical default "
39
+ "value if they use a simple DOP")
40
+ base_data_type = dop.physical_type.base_data_type
41
+ self._physical_default_value = base_data_type.from_string(
42
+ self.physical_default_value_raw)
45
43
 
46
44
  @property
47
45
  def physical_default_value(self) -> Optional[AtomicOdxType]:
@@ -55,15 +53,6 @@ class ValueParameter(ParameterWithDOP):
55
53
  def is_settable(self) -> bool:
56
54
  return True
57
55
 
58
- def get_coded_value(self, physical_value: Optional[AtomicOdxType] = None):
59
- if physical_value is not None:
60
- dop = odxrequire(self.dop)
61
- if not isinstance(dop, DataObjectProperty):
62
- odxraise()
63
- return dop.convert_physical_to_internal(physical_value)
64
- else:
65
- return self.physical_default_value
66
-
67
56
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
68
57
  physical_value = encode_state.parameter_values.get(self.short_name,
69
58
  self.physical_default_value)
@@ -77,7 +66,3 @@ class ValueParameter(ParameterWithDOP):
77
66
  bit_position_int = self.bit_position if self.bit_position is not None else 0
78
67
  return dop.convert_physical_to_bytes(
79
68
  physical_value, encode_state=encode_state, bit_position=bit_position_int)
80
-
81
- def get_valid_physical_values(self):
82
- if isinstance(self.dop, DataObjectProperty):
83
- return self.dop.get_valid_physical_values()
@@ -1,13 +1,13 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, Any, Dict, Tuple
4
4
 
5
5
  from .decodestate import DecodeState
6
6
  from .diagcodedtype import DctType, DiagCodedType
7
7
  from .encodestate import EncodeState
8
8
  from .exceptions import odxraise
9
- from .odxlink import OdxLinkDatabase, OdxLinkRef
10
- from .odxtypes import DataType
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .odxtypes import AtomicOdxType, DataType
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from .diaglayer import DiagLayer
@@ -23,7 +23,7 @@ class ParamLengthInfoType(DiagCodedType):
23
23
  def dct_type(self) -> DctType:
24
24
  return "PARAM-LENGTH-INFO-TYPE"
25
25
 
26
- def _build_odxlinks(self):
26
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
27
27
  return super()._build_odxlinks()
28
28
 
29
29
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
@@ -40,7 +40,7 @@ class ParamLengthInfoType(DiagCodedType):
40
40
  def length_key(self) -> "LengthKeyParameter":
41
41
  return self._length_key
42
42
 
43
- def convert_internal_to_bytes(self, internal_value, encode_state: EncodeState,
43
+ def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
44
44
  bit_position: int) -> bytes:
45
45
  bit_length = encode_state.parameter_values.get(self.length_key.short_name, None)
46
46
 
@@ -50,9 +50,9 @@ class ParamLengthInfoType(DiagCodedType):
50
50
  DataType.A_ASCIISTRING,
51
51
  DataType.A_UTF8STRING,
52
52
  ]:
53
- bit_length = 8 * len(internal_value)
53
+ bit_length = 8 * len(internal_value) # type: ignore[arg-type]
54
54
  if self.base_data_type in [DataType.A_UNICODE2STRING]:
55
- bit_length = 16 * len(internal_value)
55
+ bit_length = 16 * len(internal_value) # type: ignore[arg-type]
56
56
 
57
57
  if self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
58
58
  bit_length = int(internal_value).bit_length()
@@ -74,7 +74,9 @@ class ParamLengthInfoType(DiagCodedType):
74
74
  is_highlow_byte_order=self.is_highlow_byte_order,
75
75
  )
76
76
 
77
- def convert_bytes_to_internal(self, decode_state: DecodeState, bit_position: int = 0):
77
+ def convert_bytes_to_internal(self,
78
+ decode_state: DecodeState,
79
+ bit_position: int = 0) -> Tuple[AtomicOdxType, int]:
78
80
  # Find length key with matching ID.
79
81
  bit_length = None
80
82
  for parameter_name, value in decode_state.parameter_values.items():
@@ -91,7 +93,7 @@ class ParamLengthInfoType(DiagCodedType):
91
93
  # Extract the internal value and return.
92
94
  return self._extract_internal(
93
95
  decode_state.coded_message,
94
- decode_state.next_byte_position,
96
+ decode_state.cursor_position,
95
97
  bit_position,
96
98
  bit_length,
97
99
  self.base_data_type,
@@ -58,7 +58,7 @@ class PhysicalDimension(IdentifiableElement):
58
58
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
59
59
  oid = et_element.get("OID")
60
60
 
61
- def read_optional_int(element, name):
61
+ def read_optional_int(element: ElementTree.Element, name: str) -> int:
62
62
  if val_str := element.findtext(name):
63
63
  return int(val_str)
64
64
  else:
odxtools/physicaltype.py CHANGED
@@ -51,13 +51,13 @@ class PhysicalType:
51
51
  The precision is only applicable if the base data type is A_FLOAT32 or A_FLOAT64.
52
52
  """
53
53
 
54
- def __post_init__(self):
54
+ def __post_init__(self) -> None:
55
55
  self.base_data_type = DataType(self.base_data_type)
56
56
  if self.display_radix is not None:
57
57
  self.display_radix = Radix(self.display_radix)
58
58
 
59
59
  @staticmethod
60
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]):
60
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "PhysicalType":
61
61
  base_data_type_str = et_element.get("BASE-DATA-TYPE")
62
62
  if base_data_type_str not in DataType.__members__:
63
63
  odxraise(f"Encountered unknown base data type '{base_data_type_str}'")
odxtools/response.py CHANGED
@@ -3,17 +3,21 @@ from dataclasses import dataclass
3
3
  from typing import Optional
4
4
 
5
5
  from .basicstructure import BasicStructure
6
+ from .odxtypes import ParameterValue
7
+ from .parameters.matchingrequestparameter import MatchingRequestParameter
6
8
 
7
9
 
8
10
  @dataclass
9
11
  class Response(BasicStructure):
10
12
  response_type: str # "POS-RESPONSE" or "NEG-RESPONSE"
11
13
 
12
- def encode(self, coded_request: Optional[bytes] = None, **params) -> bytes:
14
+ def encode(self, coded_request: Optional[bytes] = None, **params: ParameterValue) -> bytes:
13
15
  if coded_request is not None:
14
- # Extract MATCHING-REQUEST-PARAMs from the coded request
16
+ # Extract MATCHING-REQUEST-PARAMs from the coded
17
+ # request. TODO: this should be done by
18
+ # MatchingRequestParam itself!
15
19
  for param in self.parameters:
16
- if param.parameter_type == "MATCHING-REQUEST-PARAM":
20
+ if isinstance(param, MatchingRequestParameter):
17
21
  byte_pos = param.request_byte_position
18
22
  byte_length = param.byte_length
19
23
 
odxtools/singleecujob.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from itertools import chain
5
4
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
6
5
  from xml.etree import ElementTree
7
6
 
@@ -17,7 +16,7 @@ from .message import Message
17
16
  from .nameditemlist import NamedItemList
18
17
  from .negoutputparam import NegOutputParam
19
18
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
20
- from .odxtypes import odxstr_to_bool
19
+ from .odxtypes import ParameterValue, odxstr_to_bool
21
20
  from .outputparam import OutputParam
22
21
  from .progcode import ProgCode
23
22
  from .specialdatagroup import SpecialDataGroup
@@ -71,21 +70,21 @@ class SingleEcuJob(IdentifiableElement):
71
70
  sdgs: List[SpecialDataGroup]
72
71
 
73
72
  @property
74
- def is_mandatory(self):
75
- return self.is_mandatory_raw == True
73
+ def is_mandatory(self) -> bool:
74
+ return self.is_mandatory_raw is True
76
75
 
77
76
  @property
78
- def is_executable(self):
77
+ def is_executable(self) -> bool:
79
78
  return self.is_executable_raw in (None, True)
80
79
 
81
80
  @property
82
- def is_final(self):
83
- return self.is_final_raw == True
81
+ def is_final(self) -> bool:
82
+ return self.is_final_raw is True
84
83
 
85
84
  def __post_init__(self) -> None:
86
85
  if not self.functional_class_refs:
87
86
  self.functional_class_refs = []
88
- self._functional_classes: Optional[NamedItemList[FunctionalClass]] = None
87
+ self._functional_classes: NamedItemList[FunctionalClass]
89
88
 
90
89
  # Replace None attributes by empty lists
91
90
  if not self.input_params:
@@ -164,7 +163,7 @@ class SingleEcuJob(IdentifiableElement):
164
163
  **kwargs)
165
164
 
166
165
  @property
167
- def functional_classes(self) -> Optional[NamedItemList[FunctionalClass]]:
166
+ def functional_classes(self) -> NamedItemList[FunctionalClass]:
168
167
  """The functional classes referenced by this job.
169
168
  This is None iff the references were not resolved.
170
169
  """
@@ -173,9 +172,16 @@ class SingleEcuJob(IdentifiableElement):
173
172
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
174
173
  result = {self.odx_id: self}
175
174
 
176
- for obj in chain(self.prog_codes, self.input_params, self.output_params,
177
- self.neg_output_params, self.sdgs):
178
- result.update(obj._build_odxlinks())
175
+ for prog_code in self.prog_codes:
176
+ result.update(prog_code._build_odxlinks())
177
+ for input_param in self.input_params:
178
+ result.update(input_param._build_odxlinks())
179
+ for output_param in self.output_params:
180
+ result.update(output_param._build_odxlinks())
181
+ for neg_output_param in self.neg_output_params:
182
+ result.update(neg_output_param._build_odxlinks())
183
+ for sdg in self.sdgs:
184
+ result.update(sdg._build_odxlinks())
179
185
 
180
186
  if self.admin_data:
181
187
  result.update(self.admin_data._build_odxlinks())
@@ -198,9 +204,16 @@ class SingleEcuJob(IdentifiableElement):
198
204
  else:
199
205
  logger.warning(f"Functional class ID {fc_ref!r} resolved to {fc!r}.")
200
206
 
201
- for obj in chain(self.prog_codes, self.input_params, self.output_params,
202
- self.neg_output_params, self.sdgs):
203
- obj._resolve_odxlinks(odxlinks)
207
+ for prog_code in self.prog_codes:
208
+ prog_code._resolve_odxlinks(odxlinks)
209
+ for input_param in self.input_params:
210
+ input_param._resolve_odxlinks(odxlinks)
211
+ for output_param in self.output_params:
212
+ output_param._resolve_odxlinks(odxlinks)
213
+ for neg_output_param in self.neg_output_params:
214
+ neg_output_param._resolve_odxlinks(odxlinks)
215
+ for sdg in self.sdgs:
216
+ sdg._resolve_odxlinks(odxlinks)
204
217
 
205
218
  # Resolve references of admin data
206
219
  if self.admin_data:
@@ -211,9 +224,16 @@ class SingleEcuJob(IdentifiableElement):
211
224
  self.audience._resolve_odxlinks(odxlinks)
212
225
 
213
226
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
214
- for obj in chain(self.prog_codes, self.input_params, self.output_params,
215
- self.neg_output_params, self.sdgs):
216
- obj._resolve_snrefs(diag_layer)
227
+ for prog_code in self.prog_codes:
228
+ prog_code._resolve_snrefs(diag_layer)
229
+ for input_param in self.input_params:
230
+ input_param._resolve_snrefs(diag_layer)
231
+ for output_param in self.output_params:
232
+ output_param._resolve_snrefs(diag_layer)
233
+ for neg_output_param in self.neg_output_params:
234
+ neg_output_param._resolve_snrefs(diag_layer)
235
+ for sdg in self.sdgs:
236
+ sdg._resolve_snrefs(diag_layer)
217
237
 
218
238
  # Resolve references of admin data
219
239
  if self.admin_data:
@@ -231,7 +251,7 @@ class SingleEcuJob(IdentifiableElement):
231
251
  f"Single ECU jobs are completely executed on the tester and thus cannot be decoded."
232
252
  f" You tried to decode a response for the job {self.odx_id}.")
233
253
 
234
- def encode_request(self, **params):
254
+ def encode_request(self, **params: ParameterValue) -> bytes:
235
255
  """This function's signature matches `DiagService.encode_request`
236
256
  and only raises an informative error.
237
257
  """
@@ -239,7 +259,10 @@ class SingleEcuJob(IdentifiableElement):
239
259
  f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
240
260
  f" You tried to encode a request for the job {self.odx_id}.")
241
261
 
242
- def encode_positive_response(self, coded_request, response_index=0, **params):
262
+ def encode_positive_response(self,
263
+ coded_request: bytes,
264
+ response_index: int = 0,
265
+ **params: ParameterValue) -> bytes:
243
266
  """This function's signature matches `DiagService.encode_positive_response`
244
267
  and only raises an informative error.
245
268
  """
@@ -247,7 +270,10 @@ class SingleEcuJob(IdentifiableElement):
247
270
  f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
248
271
  f" You tried to encode a response for the job {self.odx_id}.")
249
272
 
250
- def encode_negative_response(self, coded_request, response_index=0, **params):
273
+ def encode_negative_response(self,
274
+ coded_request: bytes,
275
+ response_index: int = 0,
276
+ **params: ParameterValue) -> bytes:
251
277
  """This function's signature matches `DiagService.encode_negative_response`
252
278
  and only raises an informative error.
253
279
  """
@@ -255,7 +281,7 @@ class SingleEcuJob(IdentifiableElement):
255
281
  f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
256
282
  f" You tried to encode the job {self.odx_id}.")
257
283
 
258
- def __call__(self, **params) -> bytes:
284
+ def __call__(self, **params: ParameterValue) -> bytes:
259
285
  raise EncodeError(
260
286
  f"Single ECU jobs are completely executed on the tester and thus cannot be encoded."
261
287
  f" You tried to call the job {self.odx_id}.")
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Optional
3
+ from typing import Optional, Tuple
4
4
 
5
5
  from .decodestate import DecodeState
6
6
  from .diagcodedtype import DctType, DiagCodedType
@@ -20,7 +20,7 @@ class StandardLengthType(DiagCodedType):
20
20
  def dct_type(self) -> DctType:
21
21
  return "STANDARD-LENGTH-TYPE"
22
22
 
23
- def __post_init__(self):
23
+ def __post_init__(self) -> None:
24
24
  if self.bit_mask is not None:
25
25
  maskable_types = (DataType.A_UINT32, DataType.A_INT32, DataType.A_BYTEFIELD)
26
26
  odxassert(
@@ -43,6 +43,9 @@ class StandardLengthType(DiagCodedType):
43
43
  odxraise(f'Can not apply a bit_mask on a value of type {type(internal_value)}')
44
44
  return internal_value
45
45
 
46
+ def get_static_bit_length(self) -> Optional[int]:
47
+ return self.bit_length
48
+
46
49
  def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
47
50
  bit_position: int) -> bytes:
48
51
  return self._to_bytes(
@@ -53,14 +56,16 @@ class StandardLengthType(DiagCodedType):
53
56
  is_highlow_byte_order=self.is_highlow_byte_order,
54
57
  )
55
58
 
56
- def convert_bytes_to_internal(self, decode_state: DecodeState, bit_position: int = 0):
57
- internal_value, next_byte_position = self._extract_internal(
59
+ def convert_bytes_to_internal(self,
60
+ decode_state: DecodeState,
61
+ bit_position: int = 0) -> Tuple[AtomicOdxType, int]:
62
+ internal_value, cursor_position = self._extract_internal(
58
63
  decode_state.coded_message,
59
- decode_state.next_byte_position,
64
+ decode_state.cursor_position,
60
65
  bit_position,
61
66
  self.bit_length,
62
67
  self.base_data_type,
63
68
  self.is_highlow_byte_order,
64
69
  )
65
70
  internal_value = self.__apply_mask(internal_value)
66
- return internal_value, next_byte_position
71
+ return internal_value, cursor_position
odxtools/uds.py CHANGED
@@ -49,7 +49,7 @@ class UDSSID(IntEnum):
49
49
 
50
50
 
51
51
  # add the OBD SIDs to the ones from UDS
52
- SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore
52
+ SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore[misc]
53
53
 
54
54
 
55
55
  class NegativeResponseCodes(IntEnum):
odxtools/unit.py CHANGED
@@ -60,7 +60,7 @@ class Unit(IdentifiableElement):
60
60
  offset_si_to_unit: Optional[float]
61
61
  physical_dimension_ref: Optional[OdxLinkRef]
62
62
 
63
- def __post_init__(self):
63
+ def __post_init__(self) -> None:
64
64
  self._physical_dimension = None
65
65
 
66
66
  @staticmethod
@@ -69,9 +69,9 @@ class Unit(IdentifiableElement):
69
69
  oid = et_element.get("OID")
70
70
  display_name = odxrequire(et_element.findtext("DISPLAY-NAME"))
71
71
 
72
- def read_optional_float(element, name):
73
- if element.findtext(name):
74
- return float(element.findtext(name))
72
+ def read_optional_float(element: ElementTree.Element, name: str) -> Optional[float]:
73
+ if (elem_str := element.findtext(name)) is not None:
74
+ return float(elem_str)
75
75
  else:
76
76
  return None
77
77
 
@@ -89,7 +89,7 @@ class Unit(IdentifiableElement):
89
89
  **kwargs)
90
90
 
91
91
  @property
92
- def physical_dimension(self) -> PhysicalDimension:
92
+ def physical_dimension(self) -> Optional[PhysicalDimension]:
93
93
  return self._physical_dimension
94
94
 
95
95
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
odxtools/unitgroup.py CHANGED
@@ -30,7 +30,7 @@ class UnitGroup(NamedElement):
30
30
  unit_refs: List[OdxLinkRef]
31
31
  oid: Optional[str]
32
32
 
33
- def __post_init__(self):
33
+ def __post_init__(self) -> None:
34
34
  self._units = NamedItemList[Unit]()
35
35
 
36
36
  @staticmethod
odxtools/unitspec.py CHANGED
@@ -33,13 +33,13 @@ class UnitSpec:
33
33
  physical_dimensions: Union[NamedItemList[PhysicalDimension], List[PhysicalDimension]]
34
34
  sdgs: List[SpecialDataGroup]
35
35
 
36
- def __post_init__(self):
36
+ def __post_init__(self) -> None:
37
37
  self.unit_groups = NamedItemList(self.unit_groups)
38
38
  self.units = NamedItemList(self.units)
39
39
  self.physical_dimensions = NamedItemList(self.physical_dimensions)
40
40
 
41
41
  @staticmethod
42
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]):
42
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "UnitSpec":
43
43
 
44
44
  unit_groups = [
45
45
  UnitGroup.from_et(el, doc_frags) for el in et_element.iterfind("UNIT-GROUPS/UNIT-GROUP")
odxtools/version.py CHANGED
@@ -1,6 +1,16 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- from __future__ import annotations
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
4
9
 
5
- __version__ = version = '5.3.1' # type: str
6
- __version_tuple__ = version_tuple = (5, 3, 1) # type: tuple[int | str, ...]
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '6.0.1'
16
+ __version_tuple__ = version_tuple = (6, 0, 1)
@@ -42,7 +42,7 @@ __templates_dir = os.path.sep.join([os.path.dirname(__module_filename), "templat
42
42
  def write_pdx_file(
43
43
  output_file_name: str,
44
44
  database: Database,
45
- auxiliary_content_specifiers: List[Tuple[str, bytes]] = [],
45
+ auxiliary_content_specifiers: Optional[List[Tuple[str, bytes]]] = None,
46
46
  templates_dir: str = __templates_dir,
47
47
  ) -> bool:
48
48
  """
@@ -50,13 +50,16 @@ def write_pdx_file(
50
50
  """
51
51
  global odxdatabase
52
52
 
53
+ if auxiliary_content_specifiers is None:
54
+ auxiliary_content_specifiers = []
55
+
53
56
  odxdatabase = database
54
57
 
55
- file_index = list()
58
+ file_index = []
56
59
  with zipfile.ZipFile(output_file_name, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
57
60
 
58
61
  # write all files in the templates directory
59
- for root, dir, files in os.walk(templates_dir):
62
+ for root, _, files in os.walk(templates_dir):
60
63
  for template_file_name in files:
61
64
  # we are not interested in the autosave garbage of
62
65
  # editors...
@@ -81,7 +84,7 @@ def write_pdx_file(
81
84
  elif template_file_name.endswith(".odx-d"):
82
85
  template_file_mime_type = "application/x-asam.odx.odx-d"
83
86
 
84
- in_path = list([root])
87
+ in_path = [root]
85
88
  in_path.append(template_file_name)
86
89
  in_file_name = os.path.sep.join(in_path)
87
90