From 2779e2a5bef4990746b90daa3431c23d013f5b1f Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 4 Jun 2024 14:27:28 +0200 Subject: [PATCH 1/4] handle auxiliary files more comprehensively this means writing the auxiliary files contained in the database as well as making the data of auxiliary files referenced by `ProgCode` objects conveniently available. This is surprisingly difficult because it implies some modifications to how a few objects are deepcopied in order to avoid coping the whole database when just copying a diagnostic layer. Signed-off-by: Andreas Lauser Signed-off-by: Gerrit Ecke --- examples/pdxcopy.py | 15 +--------- examples/somersault.pdx | Bin 25099 -> 25255 bytes examples/somersault_modified.pdx | Bin 25558 -> 25711 bytes examples/somersaultecu.py | 10 +++++-- odxtools/progcode.py | 11 ++++++- ...{index.xml.xml.jinja2 => index.xml.jinja2} | 0 odxtools/writepdxfile.py | 27 ++++++++++++------ 7 files changed, 36 insertions(+), 27 deletions(-) rename odxtools/templates/{index.xml.xml.jinja2 => index.xml.jinja2} (100%) diff --git a/examples/pdxcopy.py b/examples/pdxcopy.py index 8ade1a3d..dfdd0504 100755 --- a/examples/pdxcopy.py +++ b/examples/pdxcopy.py @@ -23,29 +23,16 @@ metavar="OUTPUT_PDX_FILE", help="Path to the where the resulting .pdx file is written", ) -argparser.add_argument( - "aux_files", - metavar="AUX_FILES", - nargs="*", - default=[], - help="The names of the auxiliary files to be included in the resulting .pdx file", -) args = argparser.parse_args() in_file_name = args.input_pdx_file out_file_name = args.output_pdx_file -aux_file_names = args.aux_files - -# a content specifier is a tuple of (name_in_zipfile, -# content_data). Here we simply read all all content from files on -# the filesystem... -auxiliary_content = [(x, open(x, "rb").read()) for x in aux_file_names] print(f"Loading input file '{in_file_name}'...", end="", flush=True) db = odxtools.load_pdx_file(in_file_name) print(" done") print(f"Writing output file '{out_file_name}'...", end="", flush=True) -odxtools.write_pdx_file(out_file_name, db, auxiliary_content) +odxtools.write_pdx_file(out_file_name, db) print(" done") diff --git a/examples/somersault.pdx b/examples/somersault.pdx index 25c9cceba032b022ee2141364eab83b827034171..ba04dc65b2f27ea4fc924faccd322842756ab8e0 100644 GIT binary patch delta 6615 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 bytes: + return self._code + @staticmethod def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "ProgCode": code_file = odxrequire(et_element.findtext("CODE-FILE")) @@ -52,4 +56,9 @@ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: pass def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None: - pass + db = diag_layer._database + + self._code = odxrequire( + db.auxiliary_files.get(self.code_file), + f"Reference to auxiliary file '{self.code_file}' " + f"could not be resolved") diff --git a/odxtools/templates/index.xml.xml.jinja2 b/odxtools/templates/index.xml.jinja2 similarity index 100% rename from odxtools/templates/index.xml.xml.jinja2 rename to odxtools/templates/index.xml.jinja2 diff --git a/odxtools/writepdxfile.py b/odxtools/writepdxfile.py index ed614e39..a9e7612f 100644 --- a/odxtools/writepdxfile.py +++ b/odxtools/writepdxfile.py @@ -1,10 +1,11 @@ # SPDX-License-Identifier: MIT import datetime import inspect +import mimetypes import os import time import zipfile -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, Optional import jinja2 @@ -60,7 +61,6 @@ def make_bool_xml_attrib(attrib_name: str, attrib_val: Optional[bool]) -> str: def write_pdx_file( output_file_name: str, database: Database, - auxiliary_content_specifiers: Optional[List[Tuple[str, bytes]]] = None, templates_dir: str = __templates_dir, ) -> bool: """ @@ -68,9 +68,6 @@ def write_pdx_file( """ global odxdatabase - if auxiliary_content_specifiers is None: - auxiliary_content_specifiers = [] - odxdatabase = database file_index = [] @@ -96,12 +93,18 @@ def write_pdx_file( # are written based on the database) continue - template_file_mime_type = "text/plain" + template_file_mime_type = None if template_file_name.endswith(".odx-cs"): template_file_mime_type = "application/x-asam.odx.odx-cs" elif template_file_name.endswith(".odx-d"): template_file_mime_type = "application/x-asam.odx.odx-d" + guessed_mime_type, guessed_encoding = mimetypes.guess_type(template_file_name) + if template_file_mime_type is None and guessed_mime_type is not None: + template_file_mime_type = guessed_mime_type + else: + template_file_mime_type = "application/octet-stream" + in_path = [root] in_path.append(template_file_name) in_file_name = os.path.sep.join(in_path) @@ -115,16 +118,22 @@ def write_pdx_file( out_file.write(open(in_file_name, "rb").read()) # write the auxiliary files - for output_file_name, data in auxiliary_content_specifiers: + for output_file_name, data in database.auxiliary_files.items(): file_cdate = datetime.datetime.fromtimestamp(time.time()) creation_date = file_cdate.strftime("%Y-%m-%dT%H:%M:%S") - mime_type = "text/plain" + mime_type = None if output_file_name.endswith(".odx-cs"): mime_type = "application/x-asam.odx.odx-cs" elif output_file_name.endswith(".odx-d"): mime_type = "application/x-asam.odx.odx-d" + guessed_mime_type, guessed_encoding = mimetypes.guess_type(output_file_name) + if mime_type is None and guessed_mime_type is not None: + mime_type = guessed_mime_type + else: + mime_type = "application/octet-stream" + zf_name = os.path.basename(output_file_name) with zf.open(zf_name, "w") as out_file: file_index.append((zf_name, creation_date, mime_type)) @@ -187,7 +196,7 @@ def write_pdx_file( # write the index.xml file vars["file_index"] = file_index - index_tpl = jinja_env.get_template("index.xml.xml.jinja2") + index_tpl = jinja_env.get_template("index.xml.jinja2") text = index_tpl.render(**vars) zf.writestr("index.xml", text) From 76364d88355df30348ede7fdf598d0271118f585 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 6 Jun 2024 09:24:38 +0200 Subject: [PATCH 2/4] slightly simplify the function for loading PDX files - the list of files are no longer sorted before they are processed. IIRC, this was done to make the debug output in the early days of odxtools slightly easier to interpret - directly use the file-like object from the `ZipFile` object to create the `ElementTree` object instead of deflating the contents of the zip archive's member and parsing the XML of the resulting string. This potentially saves a bit of memory (strangely enough, it does not seem to do so based on my limited testing) Signed-off-by: Andreas Lauser Signed-off-by: Gerrit Ecke --- odxtools/database.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/odxtools/database.py b/odxtools/database.py index 8d144ff4..b631d158 100644 --- a/odxtools/database.py +++ b/odxtools/database.py @@ -37,16 +37,14 @@ def __init__(self, def add_pdx_file(self, pdx_file_name: str) -> None: pdx_zip = ZipFile(pdx_file_name) - names = list(pdx_zip.namelist()) - names.sort() - for zip_member in names: + for zip_member in pdx_zip.namelist(): # The name of ODX files can end with .odx, .odx-d, # .odx-c, .odx-cs, .odx-e, .odx-f, .odx-fd, .odx-m, # .odx-v . We could test for all that, or just make # sure that the file's suffix starts with .odx p = Path(zip_member) if p.suffix.lower().startswith(".odx"): - root = ElementTree.fromstring(pdx_zip.read(zip_member)) + root = ElementTree.parse(pdx_zip.open(zip_member)).getroot() self._process_xml_tree(root) elif p.name.lower() != "index.xml": self.add_auxiliary_file(zip_member, pdx_zip.read(zip_member)) From 63958a8eb26d85648369c830ab22208a6b710884 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 6 Jun 2024 09:36:43 +0200 Subject: [PATCH 3/4] Database: set the model version correctly this is a drive-by, IMO it is too trivial to warrant a separate PR... Signed-off-by: Andreas Lauser Signed-off-by: Gerrit Ecke --- odxtools/database.py | 2 +- tests/test_somersault.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/odxtools/database.py b/odxtools/database.py index b631d158..521a5fb8 100644 --- a/odxtools/database.py +++ b/odxtools/database.py @@ -69,7 +69,7 @@ def _process_xml_tree(self, root: ElementTree.Element) -> None: odxraise(f"Different ODX versions used for the same database (ODX {model_version} " f"and ODX {self.model_version}") - self.model_version = model_version + self.model_version = model_version dlc = root.find("DIAG-LAYER-CONTAINER") if dlc is not None: diff --git a/tests/test_somersault.py b/tests/test_somersault.py index da98c297..cbcd37ea 100644 --- a/tests/test_somersault.py +++ b/tests/test_somersault.py @@ -3,6 +3,8 @@ from io import StringIO from unittest.mock import patch +from packaging.version import Version + from odxtools.description import Description from odxtools.exceptions import OdxError, odxrequire from odxtools.loadfile import load_pdx_file @@ -14,6 +16,7 @@ class TestDatabase(unittest.TestCase): def test_db_structure(self) -> None: + self.assertEqual(odxdb.model_version, Version("2.2.0")) self.assertEqual([x.short_name for x in odxdb.diag_layer_containers], ["somersault"]) self.assertEqual( From 776d7405b98e71370a7afa4fe7622a60af2b4f9c Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Mon, 10 Jun 2024 11:55:44 +0200 Subject: [PATCH 4/4] do not greedily load the contents of auxiliary files as [at]kayoub5 rightfully noted, some types of ODX files (usually `.odx-f`) may feature auxiliary files that might be several Gigabytes in size. What we do instead is to store file handles to these files. (`ZipFile` thankfully provides that without having to uncompress the whole archive.) This approach comes with the disadvantage that these files must not be modified on disk while they are used by odxtools, but IMO this is trade-off is worthwhile. thanks to [at]kayoub5 for his insistence. Signed-off-by: Andreas Lauser Signed-off-by: Gerrit Ecke --- examples/somersaultecu.py | 6 ++++-- odxtools/database.py | 16 +++++++++------- odxtools/loadfile.py | 2 +- odxtools/progcode.py | 18 ++++++++++++------ odxtools/writepdxfile.py | 4 ++-- tests/test_singleecujob.py | 3 ++- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/examples/somersaultecu.py b/examples/somersaultecu.py index 99ba9083..f07daade 100755 --- a/examples/somersaultecu.py +++ b/examples/somersaultecu.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT import pathlib from enum import IntEnum +from io import BytesIO from itertools import chain from typing import Any, Dict from xml.etree import ElementTree @@ -2467,10 +2468,11 @@ class SomersaultSID(IntEnum): database = Database() database._diag_layer_containers = NamedItemList([somersault_dlc]) database._comparam_subsets = NamedItemList(comparam_subsets) -database.add_auxiliary_file("jobs.py", b""" +database.add_auxiliary_file("jobs.py", + BytesIO(b""" def compulsory_program(): print("Hello, World") -""") +""")) # Create ID mapping and resolve references database.refresh() diff --git a/odxtools/database.py b/odxtools/database.py index 521a5fb8..be6aedd0 100644 --- a/odxtools/database.py +++ b/odxtools/database.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT from itertools import chain from pathlib import Path -from typing import List, Optional, OrderedDict +from typing import IO, List, Optional, OrderedDict from xml.etree import ElementTree from zipfile import ZipFile @@ -27,7 +27,7 @@ def __init__(self, pdx_zip: Optional[ZipFile] = None, odx_d_file_name: Optional[str] = None) -> None: self.model_version: Optional[Version] = None - self.auxiliary_files: OrderedDict[str, bytes] = OrderedDict() + self.auxiliary_files: OrderedDict[str, IO[bytes]] = OrderedDict() # create an empty database object self._diag_layer_containers = NamedItemList[DiagLayerContainer]() @@ -47,16 +47,18 @@ def add_pdx_file(self, pdx_file_name: str) -> None: root = ElementTree.parse(pdx_zip.open(zip_member)).getroot() self._process_xml_tree(root) elif p.name.lower() != "index.xml": - self.add_auxiliary_file(zip_member, pdx_zip.read(zip_member)) + self.add_auxiliary_file(zip_member, pdx_zip.open(zip_member)) def add_odx_file(self, odx_file_name: str) -> None: self._process_xml_tree(ElementTree.parse(odx_file_name).getroot()) - def add_auxiliary_file(self, aux_file_name: str, aux_file_data: Optional[bytes] = None) -> None: - if aux_file_data is None: - aux_file_data = open(aux_file_name, "rb").read() + def add_auxiliary_file(self, + aux_file_name: str, + aux_file_obj: Optional[IO[bytes]] = None) -> None: + if aux_file_obj is None: + aux_file_obj = open(aux_file_name, "rb") - self.auxiliary_files[aux_file_name] = aux_file_data + self.auxiliary_files[aux_file_name] = aux_file_obj def _process_xml_tree(self, root: ElementTree.Element) -> None: dlcs: List[DiagLayerContainer] = [] diff --git a/odxtools/loadfile.py b/odxtools/loadfile.py index cad54af8..6a7884a5 100644 --- a/odxtools/loadfile.py +++ b/odxtools/loadfile.py @@ -58,7 +58,7 @@ def load_directory(dir_name: Union[str, Path]) -> Database: elif p.suffix.lower().startswith(".odx"): db.add_odx_file(str(p)) elif p.name.lower() != "index.xml": - db.add_auxiliary_file(p.name, open(str(p), "rb").read()) + db.add_auxiliary_file(p.name, open(str(p), "rb")) db.refresh() return db diff --git a/odxtools/progcode.py b/odxtools/progcode.py index 191b4416..1c5ea69b 100644 --- a/odxtools/progcode.py +++ b/odxtools/progcode.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: MIT from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast from xml.etree import ElementTree -from .exceptions import odxrequire +from .exceptions import odxraise, odxrequire from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef if TYPE_CHECKING: @@ -58,7 +58,13 @@ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None: db = diag_layer._database - self._code = odxrequire( - db.auxiliary_files.get(self.code_file), - f"Reference to auxiliary file '{self.code_file}' " - f"could not be resolved") + aux_file = db.auxiliary_files.get(self.code_file) + + if aux_file is None: + odxraise(f"Reference to auxiliary file '{self.code_file}' " + f"could not be resolved") + self._code: bytes = cast(bytes, None) + return + + self._code = aux_file.read() + aux_file.seek(0) diff --git a/odxtools/writepdxfile.py b/odxtools/writepdxfile.py index a9e7612f..7ad05869 100644 --- a/odxtools/writepdxfile.py +++ b/odxtools/writepdxfile.py @@ -118,7 +118,7 @@ def write_pdx_file( out_file.write(open(in_file_name, "rb").read()) # write the auxiliary files - for output_file_name, data in database.auxiliary_files.items(): + for output_file_name, data_file in database.auxiliary_files.items(): file_cdate = datetime.datetime.fromtimestamp(time.time()) creation_date = file_cdate.strftime("%Y-%m-%dT%H:%M:%S") @@ -137,7 +137,7 @@ def write_pdx_file( zf_name = os.path.basename(output_file_name) with zf.open(zf_name, "w") as out_file: file_index.append((zf_name, creation_date, mime_type)) - out_file.write(data) + out_file.write(data_file.read()) jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(templates_dir)) jinja_env.globals["hasattr"] = hasattr diff --git a/tests/test_singleecujob.py b/tests/test_singleecujob.py index ad30a20c..d193607c 100644 --- a/tests/test_singleecujob.py +++ b/tests/test_singleecujob.py @@ -2,6 +2,7 @@ import inspect import os import unittest +from io import BytesIO from typing import NamedTuple, cast from xml.etree import ElementTree @@ -468,7 +469,7 @@ def test_resolve_odxlinks(self) -> None: db = Database() db.add_auxiliary_file("abc.jar", - b"this is supposed to be a JAR archive, but it isn't (HARR)") + BytesIO(b"this is supposed to be a JAR archive, but it isn't (HARR)")) dl._resolve_odxlinks(odxlinks) dl._finalize_init(db, odxlinks)