Coverage for src/pyTRLCConverter/base_converter.py: 72%
64 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 10:59 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 10:59 +0000
1""" Converter base class which does the argparser handling and provides helper functions.
3 Author: Norbert Schulz (norbert.schulz@newtec.de)
4"""
6# pyTRLCConverter - A tool to convert TRLC files to specific formats.
7# Copyright (c) 2024 - 2025 NewTec GmbH
8#
9# This file is part of pyTRLCConverter program.
10#
11# The pyTRLCConverter program is free software: you can redistribute it and/or modify it under
12# the terms of the GNU General Public License as published by the Free Software Foundation,
13# either version 3 of the License, or (at your option) any later version.
14#
15# The pyTRLCConverter program is distributed in the hope that it will be useful, but
16# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License along with pyTRLCConverter.
20# If not, see <https://www.gnu.org/licenses/>.
22# Imports **********************************************************************
23from enum import Enum
24from typing import Optional
25from pyTRLCConverter.abstract_converter import AbstractConverter
26from pyTRLCConverter.ret import Ret
27from pyTRLCConverter.trlc_helper import Record_Object
28from pyTRLCConverter.translator import Translator
29from pyTRLCConverter.logger import log_error
31# Variables ********************************************************************
33# Classes **********************************************************************
36class RecordsPolicy(Enum):
37 """
38 Enum class to define policies for handling records during conversion.
40 Attributes:
41 RECORD_CONVERT_ALL (int): Convert all records, including undefined ones
42 using convert_record_object_generic().
43 RECORD_SKIP_UNDEFINED (int): Skip records types that are not linked to a handler.
44 """
45 RECORD_CONVERT_ALL= 1
46 RECORD_SKIP_UNDEFINED = 2
49class BaseConverter(AbstractConverter):
50 # lobster-trace: SwRequirements.sw_req_destination_format
51 # lobster-trace: SwRequirements.sw_req_translation
52 """
53 Base converter with empty method implementations and helper functions
54 for subclassing converters.
55 """
56 # Converter specific sub parser
57 _parser = None
59 # Default value used to replace empty attribute values.
60 EMPTY_ATTRIBUTE_DEFAULT = "N/A"
62 def __init__(self, args: any) -> None:
63 """
64 Initializes the converter with the given arguments.
66 Args:
67 args (any): The parsed program arguments.
68 """
70 # Store the command line arguments.
71 self._args = args
73 # Record handler dictionary for project specific record handlers.
74 self._record_handler_dict = {} # type: dict[str, callable]
76 # Set the default policy for handling records.
77 self._record_policy = RecordsPolicy.RECORD_CONVERT_ALL
79 # Set the default value for empty attributes.
80 self._empty_attribute_value = BaseConverter.EMPTY_ATTRIBUTE_DEFAULT
82 # Requirement type attribute translator.
83 self._translator = Translator()
85 @classmethod
86 def register(cls, args_parser: any) -> None:
87 """Register converter specific argument parser.
89 Args:
90 args_parser (any): Argument parser
91 """
92 BaseConverter._parser = args_parser.add_parser(
93 cls.get_subcommand(),
94 help=cls.get_description()
95 )
96 BaseConverter._parser.set_defaults(converter_class=cls)
98 def begin(self) -> Ret:
99 """ Begin the conversion process.
101 Returns:
102 Ret: Status
103 """
104 result = Ret.OK
106 if isinstance(self._args.translation, str):
107 if self._translator.load(self._args.translation) is False:
108 result = Ret.ERROR
110 return result
112 def enter_file(self, file_name: str) -> Ret:
113 """Enter a file.
115 Args:
116 file_name (str): File name
118 Returns:
119 Ret: Status
120 """
121 return Ret.OK
123 def leave_file(self, file_name: str) -> Ret:
124 """Leave a file.
126 Args:
127 file_name (str): File name
129 Returns:
130 Ret: Status
131 """
132 return Ret.OK
134 def convert_section(self, section: str, level: int) -> Ret:
135 """Process the given section item.
137 Args:
138 section (str): The section name
139 level (int): The section indentation level
141 Returns:
142 Ret: Status
143 """
144 return Ret.OK
146 def convert_record_object(self, record: Record_Object, level: int) -> Ret:
147 """Process the given record object.
149 Args:
150 record (Record_Object): The record object
151 level (int): The record level
153 Returns:
154 Ret: Status
155 """
156 # Get the record attribute translation dictionary.
157 translation = self._translator.get_translation(record.n_typ.name)
159 # Check for a specific record handler.
160 record_handler = self._record_handler_dict.get(record.n_typ.name)
161 if callable(record_handler):
162 result = record_handler(record, level, translation)
164 # Don't trust project specific handlers to return a valid status.
165 if not isinstance(result, Ret):
166 log_error(f"Invalid return value from record handler for record {record.n_typ.name}.", True)
167 result = Ret.ERROR
169 elif self._record_policy == RecordsPolicy.RECORD_CONVERT_ALL:
170 result = self.convert_record_object_generic(record, level, translation)
172 else:
173 result = Ret.OK
175 return result
177 def finish(self):
178 """Finish the conversion process.
179 """
180 return Ret.OK
182 # helpers **************************************************************
184 def convert_record_object_generic(self, record: Record_Object, level: int, translation: Optional[dict]) -> Ret:
185 """Convert a record object generically.
187 Args:
188 record (Record_Object): The record object.
189 level (int): The record level.
190 translation (Optional[dict]): Translation dictionary for the record object.
191 If None, no translation is applied.
193 Returns:
194 Ret: Status
195 """
197 raise NotImplementedError
199 def _set_project_record_handler(self, record_type: str, handler: callable) -> None:
200 """Set a project specific record handler.
202 Args:
203 record_type (str): The record type
204 handler (callable): The handler function
205 """
206 self._record_handler_dict[record_type] = handler
208 def _set_project_record_handlers(self, handlers: dict[str, callable]) -> None:
209 """Set project specific record handlers.
211 Args:
212 handler (dict[str, callable]): List of record type and handler function tuples
213 """
214 for record_type, handler in handlers.items():
215 self._set_project_record_handler(record_type, handler)
217 def _get_attribute(self, record: Record_Object, attribute_name: str) -> str:
218 """Get the attribute value from the record object.
219 If the attribute is not found or empty, return the default value.
221 Args:
222 record (Record_Object): The record object
223 attribute_name (str): The attribute name to get the value from.
225 Returns:
226 str: The attribute value.
227 """
228 record_dict = record.to_python_dict()
229 attribute_value = record_dict[attribute_name]
231 if attribute_value is None:
232 attribute_value = self._empty_attribute_value
233 elif attribute_value == "":
234 attribute_value = self._empty_attribute_value
236 return attribute_value
238# Functions ********************************************************************
240# Main *************************************************************************