Skip to content

Commit 65c36f5

Browse files
authored
Merge pull request #410 from zariiii9003/docfrags_tuple
Convert OdxLinkId.doc_fragments and OdxLinkRef.ref_docs to tuple
2 parents 4526e99 + bff4ca6 commit 65c36f5

22 files changed

+100
-132
lines changed

examples/somersaultecu.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ class SomersaultSID(IntEnum):
102102
dlc_short_name = "somersault"
103103

104104
# document fragment for everything except the communication parameters
105-
doc_frags = [OdxDocFragment(dlc_short_name, DocType.CONTAINER)]
105+
doc_frags = (OdxDocFragment(dlc_short_name, DocType.CONTAINER),)
106106

107107
# document fragments for communication parameters
108-
cp_dwcan_doc_frags = [OdxDocFragment("ISO_11898_2_DWCAN", DocType.COMPARAM_SUBSET)]
109-
cp_iso15765_2_doc_frags = [OdxDocFragment("ISO_15765_2", DocType.COMPARAM_SUBSET)]
110-
cp_iso15765_3_doc_frags = [OdxDocFragment("ISO_15765_3", DocType.COMPARAM_SUBSET)]
108+
cp_dwcan_doc_frags = (OdxDocFragment("ISO_11898_2_DWCAN", DocType.COMPARAM_SUBSET),)
109+
cp_iso15765_2_doc_frags = (OdxDocFragment("ISO_15765_2", DocType.COMPARAM_SUBSET),)
110+
cp_iso15765_3_doc_frags = (OdxDocFragment("ISO_15765_3", DocType.COMPARAM_SUBSET),)
111111

112112
##################
113113
# Base variant of Somersault ECU
@@ -1430,9 +1430,8 @@ class SomersaultSID(IntEnum):
14301430
long_name="Somersault protocol info",
14311431
description=Description.from_string(
14321432
"<p>Protocol information of the somersault ECUs &amp; cetera</p>"),
1433-
comparam_spec_ref=OdxLinkRef("CPS_ISO_15765_3_on_ISO_15765_2", [
1434-
OdxDocFragment("ISO_15765_3_on_ISO_15765_2", DocType.COMPARAM_SPEC)
1435-
]),
1433+
comparam_spec_ref=OdxLinkRef("CPS_ISO_15765_3_on_ISO_15765_2", (OdxDocFragment(
1434+
"ISO_15765_3_on_ISO_15765_2", DocType.COMPARAM_SPEC),)),
14361435
comparam_refs=somersault_comparam_refs,
14371436
)
14381437
somersault_protocol = Protocol(diag_layer_raw=somersault_protocol_raw)
@@ -1764,14 +1763,19 @@ class SomersaultSID(IntEnum):
17641763
odx_cs_root = ElementTree.parse(odx_cs_dir / odx_cs_filename).getroot()
17651764
subset = odx_cs_root.find("COMPARAM-SUBSET")
17661765
if subset is not None:
1767-
comparam_subsets.append(ComparamSubset.from_et(subset, OdxDocContext(ODX_VERSION, [])))
1766+
category_sn = odxrequire(subset.findtext("SHORT-NAME"))
1767+
context = OdxDocContext(ODX_VERSION,
1768+
(OdxDocFragment(category_sn, DocType.COMPARAM_SUBSET),))
1769+
comparam_subsets.append(ComparamSubset.from_et(subset, context))
17681770

17691771
comparam_specs = []
17701772
for odx_c_filename in ("UDSOnCAN_CPS.odx-c",):
17711773
odx_c_root = ElementTree.parse(odx_cs_dir / odx_c_filename).getroot()
17721774
subset = odx_c_root.find("COMPARAM-SPEC")
17731775
if subset is not None:
1774-
comparam_specs.append(ComparamSpec.from_et(subset, OdxDocContext(ODX_VERSION, [])))
1776+
category_sn = odxrequire(subset.findtext("SHORT-NAME"))
1777+
context = OdxDocContext(ODX_VERSION, (OdxDocFragment(category_sn, DocType.COMPARAM_SPEC),))
1778+
comparam_specs.append(ComparamSpec.from_et(subset, context))
17751779

17761780
# create a database object
17771781
database = Database()

odxtools/comparamspec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .nameditemlist import NamedItemList
77
from .odxcategory import OdxCategory
88
from .odxdoccontext import OdxDocContext
9-
from .odxlink import DocType, OdxLinkDatabase, OdxLinkId
9+
from .odxlink import OdxLinkDatabase, OdxLinkId
1010
from .protstack import ProtStack
1111
from .snrefcontext import SnRefContext
1212
from .utils import dataclass_fields_asdict
@@ -23,7 +23,7 @@ class ComparamSpec(OdxCategory):
2323
@staticmethod
2424
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ComparamSpec":
2525

26-
base_obj = OdxCategory.category_from_et(et_element, context, doc_type=DocType.COMPARAM_SPEC)
26+
base_obj = OdxCategory.from_et(et_element, context)
2727
kwargs = dataclass_fields_asdict(base_obj)
2828

2929
prot_stacks = NamedItemList([

odxtools/comparamsubset.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .nameditemlist import NamedItemList
1010
from .odxcategory import OdxCategory
1111
from .odxdoccontext import OdxDocContext
12-
from .odxlink import DocType, OdxLinkDatabase, OdxLinkId
12+
from .odxlink import OdxLinkDatabase, OdxLinkId
1313
from .snrefcontext import SnRefContext
1414
from .unitspec import UnitSpec
1515
from .utils import dataclass_fields_asdict
@@ -31,14 +31,7 @@ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Compara
3131

3232
category_attrib = et_element.attrib.get("CATEGORY")
3333

34-
# In ODX 2.0, COMPARAM-SPEC is used, whereas in ODX 2.2, it
35-
# refers to something else and has been replaced by
36-
# COMPARAM-SUBSET.
37-
# - If the 'CATEGORY' attribute is missing (ODX 2.0), use
38-
# COMPARAM_SPEC,
39-
# - else (ODX 2.2), use COMPARAM_SUBSET.
40-
doc_type = DocType.COMPARAM_SUBSET if category_attrib is not None else DocType.COMPARAM_SPEC
41-
base_obj = OdxCategory.category_from_et(et_element, context, doc_type=doc_type)
34+
base_obj = OdxCategory.from_et(et_element, context)
4235
kwargs = dataclass_fields_asdict(base_obj)
4336

4437
comparams = NamedItemList(

odxtools/database.py

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from .exceptions import odxraise, odxrequire
2222
from .nameditemlist import NamedItemList
2323
from .odxdoccontext import OdxDocContext
24-
from .odxlink import OdxLinkDatabase, OdxLinkId
24+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
2525
from .snrefcontext import SnRefContext
2626

2727

@@ -77,10 +77,6 @@ def add_auxiliary_file(self,
7777
self.auxiliary_files[str(aux_file_name)] = aux_file_obj
7878

7979
def _process_xml_tree(self, root: ElementTree.Element) -> None:
80-
dlcs: list[DiagLayerContainer] = []
81-
comparam_subsets: list[ComparamSubset] = []
82-
comparam_specs: list[ComparamSpec] = []
83-
8480
# ODX spec version
8581
model_version = Version(root.attrib.get("MODEL-VERSION", "2.0"))
8682
if self.model_version is not None and self.model_version != model_version:
@@ -89,31 +85,33 @@ def _process_xml_tree(self, root: ElementTree.Element) -> None:
8985

9086
self.model_version = model_version
9187

92-
dlc = root.find("DIAG-LAYER-CONTAINER")
93-
if dlc is not None:
94-
dlcs.append(DiagLayerContainer.from_et(dlc, OdxDocContext(model_version, [])))
95-
96-
# In ODX 2.0 there was only COMPARAM-SPEC. In ODX 2.2 the
97-
# content of COMPARAM-SPEC was moved to COMPARAM-SUBSET
98-
# and COMPARAM-SPEC became a container for PROT-STACKS and
99-
# a PROT-STACK references a list of COMPARAM-SUBSET
100-
cp_subset = root.find("COMPARAM-SUBSET")
101-
if cp_subset is not None:
102-
comparam_subsets.append(
103-
ComparamSubset.from_et(cp_subset, OdxDocContext(model_version, [])))
104-
105-
cp_spec = root.find("COMPARAM-SPEC")
106-
if cp_spec is not None:
88+
child_elements = list(root)
89+
if len(child_elements) != 1:
90+
odxraise("Each ODX document must contain exactly one category.")
91+
92+
category_et = child_elements[0]
93+
category_sn = odxrequire(category_et.findtext("SHORT-NAME"))
94+
category_tag = category_et.tag
95+
96+
if category_tag == "DIAG-LAYER-CONTAINER":
97+
context = OdxDocContext(model_version,
98+
(OdxDocFragment(category_sn, DocType.CONTAINER),))
99+
self._diag_layer_containers.append(DiagLayerContainer.from_et(category_et, context))
100+
elif category_tag == "COMPARAM-SUBSET":
101+
context = OdxDocContext(model_version,
102+
(OdxDocFragment(category_sn, DocType.COMPARAM_SUBSET),))
103+
self._comparam_subsets.append(ComparamSubset.from_et(category_et, context))
104+
elif category_tag == "COMPARAM-SPEC":
105+
# In ODX 2.0 there was only COMPARAM-SPEC. In ODX 2.2 the
106+
# content of COMPARAM-SPEC was moved to COMPARAM-SUBSET
107+
# and COMPARAM-SPEC became a container for PROT-STACKS and
108+
# a PROT-STACK references a list of COMPARAM-SUBSET
109+
context = OdxDocContext(model_version,
110+
(OdxDocFragment(category_sn, DocType.COMPARAM_SPEC),))
107111
if model_version < Version("2.2"):
108-
comparam_subsets.append(
109-
ComparamSubset.from_et(cp_spec, OdxDocContext(model_version, [])))
110-
else: # odx >= 2.2
111-
comparam_specs.append(
112-
ComparamSpec.from_et(cp_spec, OdxDocContext(model_version, [])))
113-
114-
self._diag_layer_containers.extend(dlcs)
115-
self._comparam_subsets.extend(comparam_subsets)
116-
self._comparam_specs.extend(comparam_specs)
112+
self._comparam_subsets.append(ComparamSubset.from_et(category_et, context))
113+
else:
114+
self._comparam_specs.append(ComparamSpec.from_et(category_et, context))
117115

118116
def refresh(self) -> None:
119117
# Create wrapper objects

odxtools/diaglayercontainer.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
from .diaglayers.ecuvariant import EcuVariant
1111
from .diaglayers.functionalgroup import FunctionalGroup
1212
from .diaglayers.protocol import Protocol
13+
from .exceptions import odxrequire
1314
from .nameditemlist import NamedItemList
1415
from .odxcategory import OdxCategory
1516
from .odxdoccontext import OdxDocContext
16-
from .odxlink import DocType, OdxLinkDatabase, OdxLinkId
17+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
1718
from .snrefcontext import SnRefContext
1819
from .utils import dataclass_fields_asdict
1920

@@ -43,29 +44,30 @@ def ecus(self) -> NamedItemList[EcuVariant]:
4344
@staticmethod
4445
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "DiagLayerContainer":
4546

46-
cat = OdxCategory.category_from_et(et_element, context, doc_type=DocType.CONTAINER)
47+
cat = OdxCategory.from_et(et_element, context)
4748
kwargs = dataclass_fields_asdict(cat)
4849

49-
protocols = NamedItemList([
50-
Protocol.from_et(dl_element, context)
51-
for dl_element in et_element.iterfind("PROTOCOLS/PROTOCOL")
52-
])
53-
functional_groups = NamedItemList([
54-
FunctionalGroup.from_et(dl_element, context)
55-
for dl_element in et_element.iterfind("FUNCTIONAL-GROUPS/FUNCTIONAL-GROUP")
56-
])
57-
ecu_shared_datas = NamedItemList([
58-
EcuSharedData.from_et(dl_element, context)
59-
for dl_element in et_element.iterfind("ECU-SHARED-DATAS/ECU-SHARED-DATA")
60-
])
61-
base_variants = NamedItemList([
62-
BaseVariant.from_et(dl_element, context)
63-
for dl_element in et_element.iterfind("BASE-VARIANTS/BASE-VARIANT")
64-
])
65-
ecu_variants = NamedItemList([
66-
EcuVariant.from_et(dl_element, context)
67-
for dl_element in et_element.iterfind("ECU-VARIANTS/ECU-VARIANT")
68-
])
50+
def get_layer_context(diag_layer_et: ElementTree.Element) -> OdxDocContext:
51+
layer_sn = odxrequire(diag_layer_et.findtext("SHORT-NAME"))
52+
layer_docfrag = OdxDocFragment(layer_sn, DocType.LAYER)
53+
# add layer doc fragment to container doc fragment
54+
return OdxDocContext(context.version, (context.doc_fragments[0], layer_docfrag))
55+
56+
protocols = NamedItemList(
57+
Protocol.from_et(layer_et, get_layer_context(layer_et))
58+
for layer_et in et_element.iterfind("PROTOCOLS/PROTOCOL"))
59+
functional_groups = NamedItemList(
60+
FunctionalGroup.from_et(layer_et, get_layer_context(layer_et))
61+
for layer_et in et_element.iterfind("FUNCTIONAL-GROUPS/FUNCTIONAL-GROUP"))
62+
ecu_shared_datas = NamedItemList(
63+
EcuSharedData.from_et(layer_et, get_layer_context(layer_et))
64+
for layer_et in et_element.iterfind("ECU-SHARED-DATAS/ECU-SHARED-DATA"))
65+
base_variants = NamedItemList(
66+
BaseVariant.from_et(layer_et, get_layer_context(layer_et))
67+
for layer_et in et_element.iterfind("BASE-VARIANTS/BASE-VARIANT"))
68+
ecu_variants = NamedItemList(
69+
EcuVariant.from_et(layer_et, get_layer_context(layer_et))
70+
for layer_et in et_element.iterfind("ECU-VARIANTS/ECU-VARIANT"))
6971

7072
return DiagLayerContainer(
7173
protocols=protocols,

odxtools/diaglayers/diaglayerraw.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
from ..diagdatadictionaryspec import DiagDataDictionarySpec
1111
from ..diagservice import DiagService
1212
from ..element import IdentifiableElement
13-
from ..exceptions import odxassert, odxraise, odxrequire
13+
from ..exceptions import odxassert, odxraise
1414
from ..functionalclass import FunctionalClass
1515
from ..library import Library
1616
from ..nameditemlist import NamedItemList
1717
from ..odxdoccontext import OdxDocContext
18-
from ..odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
18+
from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
1919
from ..request import Request
2020
from ..response import Response
2121
from ..singleecujob import SingleEcuJob
@@ -77,10 +77,6 @@ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "DiagLay
7777
variant_type = cast(DiagLayerType, None)
7878
odxraise(f"Encountered unknown diagnostic layer type '{et_element.tag}'")
7979

80-
short_name = odxrequire(et_element.findtext("SHORT-NAME"))
81-
82-
# extend the applicable ODX "document fragments" for the diag layer objects
83-
context.doc_fragments.append(OdxDocFragment(short_name, DocType.LAYER))
8480
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
8581

8682
admin_data = None

odxtools/diaglayers/hierarchyelement.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from .hierarchyelementraw import HierarchyElementRaw
3232

3333
if TYPE_CHECKING:
34-
from .database import Database
34+
from ..database import Database
3535
from .protocol import Protocol
3636

3737
TNamed = TypeVar("TNamed", bound=OdxNamed)

odxtools/odxcategory.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
from .admindata import AdminData
77
from .companydata import CompanyData
88
from .element import IdentifiableElement
9-
from .exceptions import odxrequire
109
from .nameditemlist import NamedItemList
1110
from .odxdoccontext import OdxDocContext
12-
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
11+
from .odxlink import OdxLinkDatabase, OdxLinkId
1312
from .snrefcontext import SnRefContext
1413
from .specialdatagroup import SpecialDataGroup
1514
from .utils import dataclass_fields_asdict
@@ -28,17 +27,7 @@ class OdxCategory(IdentifiableElement):
2827

2928
@staticmethod
3029
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "OdxCategory":
31-
raise Exception("Calling `._from_et()` is not allowed for OdxCategory. "
32-
"Use `OdxCategory.category_from_et()`!")
3330

34-
@staticmethod
35-
def category_from_et(et_element: ElementTree.Element, context: OdxDocContext, *,
36-
doc_type: DocType) -> "OdxCategory":
37-
38-
short_name = odxrequire(et_element.findtext("SHORT-NAME"))
39-
# create the current ODX "document fragment" (description of the
40-
# current document for references and IDs)
41-
context.doc_fragments.append(OdxDocFragment(short_name, doc_type))
4231
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
4332

4433
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)

odxtools/odxdoccontext.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
from typing import TYPE_CHECKING, NamedTuple
1+
from dataclasses import dataclass
2+
from typing import TYPE_CHECKING
23

34
from packaging.version import Version
45

56
if TYPE_CHECKING:
67
from odxtools.odxlink import OdxDocFragment
78

89

9-
class OdxDocContext(NamedTuple):
10+
@dataclass(slots=True, frozen=True)
11+
class OdxDocContext:
1012
version: Version
11-
doc_fragments: list["OdxDocFragment"]
13+
14+
# the doc_fragments are either tuple(doc_frag(category),)
15+
# or tuple(doc_frag(category), doc_frag(diag_layer))
16+
doc_fragments: tuple["OdxDocFragment"] | tuple["OdxDocFragment", "OdxDocFragment"]

odxtools/odxlink.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: MIT
22
import warnings
33
from collections.abc import Iterable
4-
from dataclasses import dataclass, field
4+
from dataclasses import dataclass
55
from enum import Enum
66
from typing import Any, Optional, TypeVar, overload
77
from xml.etree import ElementTree
@@ -46,7 +46,7 @@ class OdxLinkId:
4646

4747
#: The name and type of the document fragment to which the
4848
#: `local_id` is relative to
49-
doc_fragments: list[OdxDocFragment] = field(default_factory=list)
49+
doc_fragments: tuple[OdxDocFragment] | tuple[OdxDocFragment, OdxDocFragment]
5050

5151
def __hash__(self) -> int:
5252
# we do not hash about the document fragment here, because
@@ -95,7 +95,7 @@ class OdxLinkRef:
9595
ref_id: str
9696

9797
#: The document fragments to which the `ref_id` refers to (in reverse order)
98-
ref_docs: list[OdxDocFragment] = field(default_factory=list)
98+
ref_docs: tuple[OdxDocFragment, ...]
9999

100100
# TODO: this is difficult because OdxLinkRef is derived from and
101101
# we do not want having to specify it mandatorily
@@ -143,10 +143,10 @@ def from_et(et: ElementTree.Element | None, context: OdxDocContext) -> Optional[
143143
# if the target document fragment is specified by the
144144
# reference, use it, else use the document fragment containing
145145
# the reference.
146-
if docref is not None:
147-
doc_frags = [OdxDocFragment(docref, odxrequire(doctype))]
148-
else:
146+
if docref is None:
149147
doc_frags = context.doc_fragments
148+
else:
149+
doc_frags = (OdxDocFragment(docref, odxrequire(doctype)),)
150150

151151
return OdxLinkRef(id_ref, doc_frags)
152152

0 commit comments

Comments
 (0)