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/table.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, Union
2
+ from dataclasses import dataclass, field
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
@@ -8,7 +8,8 @@ from .dataobjectproperty import DataObjectProperty
8
8
  from .element import IdentifiableElement
9
9
  from .exceptions import odxassert
10
10
  from .nameditemlist import NamedItemList
11
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from .odxdoccontext import OdxDocContext
12
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
12
13
  from .snrefcontext import SnRefContext
13
14
  from .specialdatagroup import SpecialDataGroup
14
15
  from .tablediagcommconnector import TableDiagCommConnector
@@ -16,20 +17,20 @@ from .tablerow import TableRow
16
17
  from .utils import dataclass_fields_asdict
17
18
 
18
19
 
19
- @dataclass
20
+ @dataclass(kw_only=True)
20
21
  class Table(IdentifiableElement):
21
22
  """This class represents a TABLE."""
22
- key_label: Optional[str]
23
- struct_label: Optional[str]
24
- admin_data: Optional[AdminData]
25
- key_dop_ref: Optional[OdxLinkRef]
26
- table_rows_raw: List[Union[TableRow, OdxLinkRef]]
27
- table_diag_comm_connectors: List[TableDiagCommConnector]
28
- sdgs: List[SpecialDataGroup]
29
- semantic: Optional[str]
23
+ key_label: str | None = None
24
+ struct_label: str | None = None
25
+ admin_data: AdminData | None = None
26
+ key_dop_ref: OdxLinkRef | None = None
27
+ table_rows_raw: list[TableRow | OdxLinkRef] = field(default_factory=list)
28
+ table_diag_comm_connectors: list[TableDiagCommConnector] = field(default_factory=list)
29
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
30
+ semantic: str | None = None
30
31
 
31
32
  @property
32
- def key_dop(self) -> Optional[DataObjectProperty]:
33
+ def key_dop(self) -> DataObjectProperty | None:
33
34
  """The key data object property associated with this table."""
34
35
  return self._key_dop
35
36
 
@@ -39,31 +40,29 @@ class Table(IdentifiableElement):
39
40
  return self._table_rows
40
41
 
41
42
  @staticmethod
42
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Table":
43
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Table":
43
44
  """Reads a TABLE."""
44
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
45
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
45
46
  odx_id = kwargs["odx_id"]
46
47
  key_label = et_element.findtext("KEY-LABEL")
47
48
  struct_label = et_element.findtext("STRUCT-LABEL")
48
- admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
49
- key_dop_ref = OdxLinkRef.from_et(et_element.find("KEY-DOP-REF"), doc_frags)
49
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
50
+ key_dop_ref = OdxLinkRef.from_et(et_element.find("KEY-DOP-REF"), context)
50
51
 
51
- table_rows_raw: List[Union[OdxLinkRef, TableRow]] = []
52
+ table_rows_raw: list[OdxLinkRef | TableRow] = []
52
53
  for sub_elem in et_element:
53
54
  if sub_elem.tag == "TABLE-ROW":
54
55
  table_rows_raw.append(
55
56
  TableRow.tablerow_from_et(
56
- sub_elem, doc_frags, table_ref=OdxLinkRef.from_id(odx_id)))
57
+ sub_elem, context, table_ref=OdxLinkRef.from_id(odx_id)))
57
58
  elif sub_elem.tag == "TABLE-ROW-REF":
58
- table_rows_raw.append(OdxLinkRef.from_et(sub_elem, doc_frags))
59
+ table_rows_raw.append(OdxLinkRef.from_et(sub_elem, context))
59
60
 
60
61
  table_diag_comm_connectors = [
61
- TableDiagCommConnector.from_et(dcc_elem, doc_frags) for dcc_elem in et_element.iterfind(
62
+ TableDiagCommConnector.from_et(dcc_elem, context) for dcc_elem in et_element.iterfind(
62
63
  "TABLE-DIAG-COMM-CONNECTORS/TABLE-DIAG-COMM-CONNECTOR")
63
64
  ]
64
- sdgs = [
65
- SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
66
- ]
65
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
67
66
  semantic = et_element.get("SEMANTIC")
68
67
 
69
68
  return Table(
@@ -77,7 +76,7 @@ class Table(IdentifiableElement):
77
76
  semantic=semantic,
78
77
  **kwargs)
79
78
 
80
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
79
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
81
80
  result = {self.odx_id: self}
82
81
 
83
82
  for table_row_wrapper in self.table_rows_raw:
@@ -93,7 +92,7 @@ class Table(IdentifiableElement):
93
92
  return result
94
93
 
95
94
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
96
- self._key_dop: Optional[DataObjectProperty] = None
95
+ self._key_dop: DataObjectProperty | None = None
97
96
  if self.key_dop_ref is not None:
98
97
  self._key_dop = odxlinks.resolve(self.key_dop_ref, DataObjectProperty)
99
98
 
@@ -1,20 +1,21 @@
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 .diagcomm import DiagComm
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
8
+ from .odxdoccontext import OdxDocContext
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
9
10
  from .snrefcontext import SnRefContext
10
11
 
11
12
 
12
- @dataclass
13
+ @dataclass(kw_only=True)
13
14
  class TableDiagCommConnector:
14
15
  semantic: str
15
16
 
16
- diag_comm_ref: Optional[OdxLinkRef]
17
- diag_comm_snref: Optional[str]
17
+ diag_comm_ref: OdxLinkRef | None = None
18
+ diag_comm_snref: str | None = None
18
19
 
19
20
  @property
20
21
  def diag_comm(self) -> DiagComm:
@@ -22,11 +23,11 @@ class TableDiagCommConnector:
22
23
 
23
24
  @staticmethod
24
25
  def from_et(et_element: ElementTree.Element,
25
- doc_frags: List[OdxDocFragment]) -> "TableDiagCommConnector":
26
+ context: OdxDocContext) -> "TableDiagCommConnector":
26
27
 
27
28
  semantic = odxrequire(et_element.findtext("SEMANTIC"))
28
29
 
29
- diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
30
+ diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), context)
30
31
  diag_comm_snref = None
31
32
  if (dc_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
32
33
  diag_comm_snref = odxrequire(dc_snref_elem.get("SHORT-NAME"))
@@ -34,7 +35,7 @@ class TableDiagCommConnector:
34
35
  return TableDiagCommConnector(
35
36
  semantic=semantic, diag_comm_ref=diag_comm_ref, diag_comm_snref=diag_comm_snref)
36
37
 
37
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
38
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
38
39
  return {}
39
40
 
40
41
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
odxtools/tablerow.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass, fields
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
2
+ from dataclasses import dataclass, field
3
+ from typing import TYPE_CHECKING, Any, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .admindata import AdminData
@@ -11,7 +11,8 @@ from .element import IdentifiableElement
11
11
  from .exceptions import odxassert, odxraise, odxrequire
12
12
  from .functionalclass import FunctionalClass
13
13
  from .nameditemlist import NamedItemList
14
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
14
+ from .odxdoccontext import OdxDocContext
15
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
15
16
  from .odxtypes import AtomicOdxType, odxstr_to_bool
16
17
  from .preconditionstateref import PreConditionStateRef
17
18
  from .snrefcontext import SnRefContext
@@ -24,7 +25,7 @@ if TYPE_CHECKING:
24
25
  from .table import Table
25
26
 
26
27
 
27
- @dataclass
28
+ @dataclass(kw_only=True)
28
29
  class TableRow(IdentifiableElement):
29
30
  """This class represents a TABLE-ROW."""
30
31
  table_ref: OdxLinkRef
@@ -33,22 +34,22 @@ class TableRow(IdentifiableElement):
33
34
  # The spec mandates that either a structure or a non-complex DOP
34
35
  # must be referenced here, i.e., exactly one of the four
35
36
  # attributes below is not None
36
- dop_ref: Optional[OdxLinkRef]
37
- dop_snref: Optional[str]
38
- structure_ref: Optional[OdxLinkRef]
39
- structure_snref: Optional[str]
40
-
41
- sdgs: List[SpecialDataGroup]
42
- audience: Optional[Audience]
43
- functional_class_refs: List[OdxLinkRef]
44
- state_transition_refs: List[StateTransitionRef]
45
- pre_condition_state_refs: List[PreConditionStateRef]
46
- admin_data: Optional[AdminData]
47
-
48
- is_executable_raw: Optional[bool]
49
- semantic: Optional[str]
50
- is_mandatory_raw: Optional[bool]
51
- is_final_raw: Optional[bool]
37
+ dop_ref: OdxLinkRef | None = None
38
+ dop_snref: str | None = None
39
+ structure_ref: OdxLinkRef | None = None
40
+ structure_snref: str | None = None
41
+
42
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
43
+ audience: Audience | None = None
44
+ functional_class_refs: list[OdxLinkRef] = field(default_factory=list)
45
+ state_transition_refs: list[StateTransitionRef] = field(default_factory=list)
46
+ pre_condition_state_refs: list[PreConditionStateRef] = field(default_factory=list)
47
+ admin_data: AdminData | None = None
48
+
49
+ is_executable_raw: bool | None = None
50
+ semantic: str | None = None
51
+ is_mandatory_raw: bool | None = None
52
+ is_final_raw: bool | None = None
52
53
 
53
54
  @property
54
55
  def table(self) -> "Table":
@@ -57,16 +58,16 @@ class TableRow(IdentifiableElement):
57
58
  # the value of the key expressed in the type represented by the
58
59
  # referenced DOP
59
60
  @property
60
- def key(self) -> Optional[AtomicOdxType]:
61
+ def key(self) -> AtomicOdxType | None:
61
62
  return self._key
62
63
 
63
64
  @property
64
- def dop(self) -> Optional[DataObjectProperty]:
65
+ def dop(self) -> DataObjectProperty | None:
65
66
  """The data object property object resolved by dop_ref."""
66
67
  return self._dop
67
68
 
68
69
  @property
69
- def structure(self) -> Optional[Structure]:
70
+ def structure(self) -> Structure | None:
70
71
  """The structure associated with this table row."""
71
72
  return self._structure
72
73
 
@@ -87,52 +88,50 @@ class TableRow(IdentifiableElement):
87
88
  return self.is_final_raw is True
88
89
 
89
90
  @staticmethod
90
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> Any:
91
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> Any:
91
92
  raise RuntimeError(
92
93
  "Calling TableRow.from_et() is not allowed. Use TableRow.tablerow_from_et().")
93
94
 
94
95
  @staticmethod
95
- def tablerow_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
96
+ def tablerow_from_et(et_element: ElementTree.Element, context: OdxDocContext, *,
96
97
  table_ref: OdxLinkRef) -> "TableRow":
97
98
  """Reads a TABLE-ROW."""
98
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
99
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
99
100
 
100
101
  key_raw = odxrequire(et_element.findtext("KEY"))
101
102
 
102
- dop_ref = OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), doc_frags)
103
- dop_snref: Optional[str] = None
103
+ dop_ref = OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), context)
104
+ dop_snref: str | None = None
104
105
  if (dop_snref_elem := et_element.find("DATA-OBJECT-PROP-SNREF")) is not None:
105
106
  dop_snref = dop_snref_elem.attrib["SHORT-NAME"]
106
107
 
107
- structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
108
- structure_snref: Optional[str] = None
108
+ structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), context)
109
+ structure_snref: str | None = None
109
110
  if (structure_snref_elem := et_element.find("STRUCTURE-SNREF")) is not None:
110
111
  structure_snref = structure_snref_elem.attrib["SHORT-NAME"]
111
112
 
112
- sdgs = [
113
- SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
114
- ]
113
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
115
114
 
116
115
  audience = None
117
116
  if (audience_elem := et_element.find("AUDIENCE")) is not None:
118
- audience = Audience.from_et(audience_elem, doc_frags)
117
+ audience = Audience.from_et(audience_elem, context)
119
118
 
120
119
  functional_class_refs = [
121
- odxrequire(OdxLinkRef.from_et(el, doc_frags))
120
+ odxrequire(OdxLinkRef.from_et(el, context))
122
121
  for el in et_element.iterfind("FUNCT-CLASS-REFS/FUNCT-CLASS-REF")
123
122
  ]
124
123
 
125
124
  state_transition_refs = [
126
- StateTransitionRef.from_et(el, doc_frags)
125
+ StateTransitionRef.from_et(el, context)
127
126
  for el in et_element.iterfind("STATE-TRANSITION-REFS/STATE-TRANSITION-REF")
128
127
  ]
129
128
 
130
129
  pre_condition_state_refs = [
131
- PreConditionStateRef.from_et(el, doc_frags)
130
+ PreConditionStateRef.from_et(el, context)
132
131
  for el in et_element.iterfind("PRE-CONDITION-STATE-REFS/PRE-CONDITION-STATE-REF")
133
132
  ]
134
133
 
135
- admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
134
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
136
135
 
137
136
  is_executable_raw = odxstr_to_bool(et_element.attrib.get("IS-EXECUTABLE"))
138
137
  semantic = et_element.attrib.get("SEMANTIC")
@@ -159,8 +158,8 @@ class TableRow(IdentifiableElement):
159
158
  **kwargs)
160
159
 
161
160
  def __post_init__(self) -> None:
162
- self._dop: Optional[DataObjectProperty] = None
163
- self._structure: Optional[Structure] = None
161
+ self._dop: DataObjectProperty | None = None
162
+ self._structure: Structure | None = None
164
163
 
165
164
  n = sum([0 if x is None else 1 for x in (self.dop_ref, self.dop_snref)])
166
165
  odxassert(
@@ -174,7 +173,7 @@ class TableRow(IdentifiableElement):
174
173
  f"Table row {self.short_name}: The structure can either be defined using ODXLINK or SNREF but not both."
175
174
  )
176
175
 
177
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
176
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
178
177
  result = {self.odx_id: self}
179
178
 
180
179
  for sdg in self.sdgs:
@@ -258,7 +257,29 @@ class TableRow(IdentifiableElement):
258
257
  for pc_ref in self.pre_condition_state_refs:
259
258
  pc_ref._resolve_snrefs(context)
260
259
 
261
- def __reduce__(self) -> Tuple[Any, ...]:
260
+ def __reduce__(self) -> tuple[Any, ...]:
262
261
  """This ensures that the object can be correctly reconstructed during unpickling."""
263
262
  state = self.__dict__.copy()
264
- return self.__class__, tuple([getattr(self, x.name) for x in fields(self)]), state
263
+ return _reconstruct_tablerow, (self.__class__, state)
264
+
265
+ def __setstate__(self, state: dict[str, Any]) -> None:
266
+ """
267
+ Restore the object's internal state.
268
+
269
+ This method is called during the unpickling, it updates
270
+ the instance's __dict__ with the saved state.
271
+ """
272
+ self.__dict__.update(state)
273
+
274
+
275
+ def _reconstruct_tablerow(cls: Any, state: dict[str, Any]) -> TableRow:
276
+ """
277
+ Reconstruct a TableRow instance from pickled state.
278
+
279
+ This function is used during unpickling to bypass the `__init__`
280
+ constructor, which would normally enforce `kw_only=True` arguments.
281
+ Instead, it creates an uninitialized object and restores its state.
282
+ """
283
+ obj = cls.__new__(cls)
284
+ obj.__setstate__(state)
285
+ return cast(TableRow, obj)
@@ -1,18 +1,19 @@
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 NamedElement
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
8
+ from .odxdoccontext import OdxDocContext
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
9
10
  from .snrefcontext import SnRefContext
10
11
  from .table import Table
11
12
  from .tablerow import TableRow
12
13
  from .utils import dataclass_fields_asdict
13
14
 
14
15
 
15
- @dataclass
16
+ @dataclass(kw_only=True)
16
17
  class TableRowConnector(NamedElement):
17
18
  table_ref: OdxLinkRef
18
19
  table_row_snref: str
@@ -26,17 +27,16 @@ class TableRowConnector(NamedElement):
26
27
  return self._table_row
27
28
 
28
29
  @staticmethod
29
- def from_et(et_element: ElementTree.Element,
30
- doc_frags: List[OdxDocFragment]) -> "TableRowConnector":
31
- kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
30
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "TableRowConnector":
31
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
32
32
 
33
- table_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags))
33
+ table_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-REF"), context))
34
34
  table_row_snref_el = odxrequire(et_element.find("TABLE-ROW-SNREF"))
35
35
  table_row_snref = odxrequire(table_row_snref_el.get("SHORT-NAME"))
36
36
 
37
37
  return TableRowConnector(table_ref=table_ref, table_row_snref=table_row_snref, **kwargs)
38
38
 
39
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
39
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
40
40
  return {}
41
41
 
42
42
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
odxtools/teammember.py CHANGED
@@ -1,29 +1,30 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
2
+ from dataclasses import dataclass, field
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .odxdoccontext import OdxDocContext
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId
9
10
  from .snrefcontext import SnRefContext
10
11
  from .utils import dataclass_fields_asdict
11
12
 
12
13
 
13
- @dataclass
14
+ @dataclass(kw_only=True)
14
15
  class TeamMember(IdentifiableElement):
15
- roles: List[str]
16
- department: Optional[str]
17
- address: Optional[str]
18
- zipcode: Optional[str] # the tag for this is "ZIP", but `zip` is a keyword in python
19
- city: Optional[str]
20
- phone: Optional[str]
21
- fax: Optional[str]
22
- email: Optional[str]
16
+ roles: list[str] = field(default_factory=list)
17
+ department: str | None = None
18
+ address: str | None = None
19
+ zipcode: str | None = None # the tag for this is "ZIP", but `zip` is a keyword in python
20
+ city: str | None = None
21
+ phone: str | None = None
22
+ fax: str | None = None
23
+ email: str | None = None
23
24
 
24
25
  @staticmethod
25
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "TeamMember":
26
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
26
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "TeamMember":
27
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
27
28
 
28
29
  roles = [odxrequire(role_elem.text) for role_elem in et_element.iterfind("ROLES/ROLE")]
29
30
  department = et_element.findtext("DEPARTMENT")
@@ -45,7 +46,7 @@ class TeamMember(IdentifiableElement):
45
46
  email=email,
46
47
  **kwargs)
47
48
 
48
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
49
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
49
50
  result = {self.odx_id: self}
50
51
 
51
52
  return result
@@ -5,8 +5,10 @@
5
5
 
6
6
  {%- macro printParentRef(par) -%}
7
7
  <PARENT-REF ID-REF="{{par.layer.odx_id.local_id}}"
8
- DOCREF="{{get_parent_container_name(par.layer.short_name)}}"
8
+ {%- if par.layer_ref.ref_docs|length == 1 %}
9
+ DOCREF="{{par.layer_ref.ref_docs[0].doc_name}}"
9
10
  DOCTYPE="CONTAINER"
11
+ {%- endif %}
10
12
  xsi:type="{{par.layer.variant_type.value}}-REF">
11
13
  {%- if par.not_inherited_diag_comms %}
12
14
  <NOT-INHERITED-DIAG-COMMS>
odxtools/text.py CHANGED
@@ -1,17 +1,16 @@
1
1
  from dataclasses import dataclass
2
- from typing import List, Optional
3
2
  from xml.etree import ElementTree
4
3
 
5
- from .odxlink import OdxDocFragment
4
+ from .odxdoccontext import OdxDocContext
6
5
 
7
6
 
8
- @dataclass
7
+ @dataclass(kw_only=True)
9
8
  class Text:
10
9
  text: str
11
- text_identifier: Optional[str]
10
+ text_identifier: str | None = None
12
11
 
13
12
  @staticmethod
14
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Text":
13
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Text":
15
14
  # Extract the contents of the tag as a string.
16
15
  raw_string = et_element.text or ""
17
16
  for e in et_element:
odxtools/uds.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import IntEnum
3
3
  from itertools import chain
4
- from typing import Optional
5
4
 
6
5
  import odxtools.obd as obd
7
6
 
@@ -82,7 +81,7 @@ _sid_to_name = {
82
81
  SID = IntEnum("UdsSID", ((i.name, i.value) for i in chain(obd.SID, UDSSID))) # type: ignore[misc]
83
82
 
84
83
 
85
- def sid_to_name(sid: int) -> Optional[str]:
84
+ def sid_to_name(sid: int) -> str | None:
86
85
  if sid in _sid_to_name:
87
86
  return _sid_to_name[sid]
88
87
  elif 0x81 <= sid and sid <= 0x82:
@@ -142,7 +141,7 @@ def negative_response_id(service_id: int) -> int:
142
141
  return NegativeResponseId
143
142
 
144
143
 
145
- def is_response_pending(telegram_payload: bytes, request_sid: Optional[int] = None) -> bool:
144
+ def is_response_pending(telegram_payload: bytes, request_sid: int | None = None) -> bool:
146
145
  # "response pending" responses exhibit at least three bytes
147
146
  if len(telegram_payload) < 3:
148
147
  return False
odxtools/unit.py CHANGED
@@ -1,17 +1,18 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
8
+ from .odxdoccontext import OdxDocContext
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
10
  from .physicaldimension import PhysicalDimension
10
11
  from .snrefcontext import SnRefContext
11
12
  from .utils import dataclass_fields_asdict
12
13
 
13
14
 
14
- @dataclass
15
+ @dataclass(kw_only=True)
15
16
  class Unit(IdentifiableElement):
16
17
  """
17
18
  A unit consists of an ID, short name and a display name.
@@ -53,21 +54,21 @@ class Unit(IdentifiableElement):
53
54
  ```
54
55
  """
55
56
  display_name: str
56
- factor_si_to_unit: Optional[float]
57
- offset_si_to_unit: Optional[float]
58
- physical_dimension_ref: Optional[OdxLinkRef]
57
+ factor_si_to_unit: float | None = None
58
+ offset_si_to_unit: float | None = None
59
+ physical_dimension_ref: OdxLinkRef | None = None
59
60
 
60
61
  @property
61
- def physical_dimension(self) -> Optional[PhysicalDimension]:
62
+ def physical_dimension(self) -> PhysicalDimension | None:
62
63
  return self._physical_dimension
63
64
 
64
65
  @staticmethod
65
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Unit":
66
- kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
66
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Unit":
67
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
67
68
 
68
69
  display_name = odxrequire(et_element.findtext("DISPLAY-NAME"))
69
70
 
70
- def read_optional_float(element: ElementTree.Element, name: str) -> Optional[float]:
71
+ def read_optional_float(element: ElementTree.Element, name: str) -> float | None:
71
72
  if (elem_str := element.findtext(name)) is not None:
72
73
  return float(elem_str)
73
74
  else:
@@ -76,7 +77,7 @@ class Unit(IdentifiableElement):
76
77
  factor_si_to_unit = read_optional_float(et_element, "FACTOR-SI-TO-UNIT")
77
78
  offset_si_to_unit = read_optional_float(et_element, "OFFSET-SI-TO-UNIT")
78
79
  physical_dimension_ref = OdxLinkRef.from_et(
79
- et_element.find("PHYSICAL-DIMENSION-REF"), doc_frags)
80
+ et_element.find("PHYSICAL-DIMENSION-REF"), context)
80
81
 
81
82
  return Unit(
82
83
  display_name=display_name,
@@ -85,11 +86,11 @@ class Unit(IdentifiableElement):
85
86
  physical_dimension_ref=physical_dimension_ref,
86
87
  **kwargs)
87
88
 
88
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
89
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
89
90
  return {self.odx_id: self}
90
91
 
91
92
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
92
- self._physical_dimension: Optional[PhysicalDimension] = None
93
+ self._physical_dimension: PhysicalDimension | None = None
93
94
  if self.physical_dimension_ref:
94
95
  self._physical_dimension = odxlinks.resolve(self.physical_dimension_ref,
95
96
  PhysicalDimension)
odxtools/unitgroup.py CHANGED
@@ -1,35 +1,36 @@
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 .element import NamedElement
7
7
  from .exceptions import odxraise, odxrequire
8
8
  from .nameditemlist import NamedItemList
9
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
+ from .odxdoccontext import OdxDocContext
10
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
11
  from .snrefcontext import SnRefContext
11
12
  from .unit import Unit
12
13
  from .unitgroupcategory import UnitGroupCategory
13
14
  from .utils import dataclass_fields_asdict
14
15
 
15
16
 
16
- @dataclass
17
+ @dataclass(kw_only=True)
17
18
  class UnitGroup(NamedElement):
18
19
  """A group of units.
19
20
 
20
21
  There are two categories of groups: COUNTRY and EQUIV-UNITS.
21
22
  """
22
23
  category: UnitGroupCategory
23
- unit_refs: List[OdxLinkRef]
24
- oid: Optional[str]
24
+ unit_refs: list[OdxLinkRef] = field(default_factory=list)
25
+ oid: str | None = None
25
26
 
26
27
  @property
27
28
  def units(self) -> NamedItemList[Unit]:
28
29
  return self._units
29
30
 
30
31
  @staticmethod
31
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "UnitGroup":
32
- kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
32
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "UnitGroup":
33
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
33
34
 
34
35
  category_str = odxrequire(et_element.findtext("CATEGORY"))
35
36
  try:
@@ -39,14 +40,14 @@ class UnitGroup(NamedElement):
39
40
  odxraise(f"Encountered unknown unit group category '{category_str}'")
40
41
 
41
42
  unit_refs = [
42
- odxrequire(OdxLinkRef.from_et(el, doc_frags))
43
+ odxrequire(OdxLinkRef.from_et(el, context))
43
44
  for el in et_element.iterfind("UNIT-REFS/UNIT-REF")
44
45
  ]
45
46
  oid = et_element.get("OID")
46
47
 
47
48
  return UnitGroup(category=category, unit_refs=unit_refs, oid=oid, **kwargs)
48
49
 
49
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
50
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
50
51
  return {}
51
52
 
52
53
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: