fameio 3.1.1__py3-none-any.whl → 3.3.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.
- fameio/cli/convert_results.py +10 -10
- fameio/cli/make_config.py +9 -9
- fameio/cli/options.py +6 -4
- fameio/cli/parser.py +87 -51
- fameio/cli/reformat.py +58 -0
- fameio/input/__init__.py +4 -4
- fameio/input/loader/__init__.py +13 -13
- fameio/input/loader/controller.py +64 -18
- fameio/input/loader/loader.py +25 -16
- fameio/input/metadata.py +57 -38
- fameio/input/resolver.py +9 -10
- fameio/input/scenario/agent.py +62 -26
- fameio/input/scenario/attribute.py +93 -40
- fameio/input/scenario/contract.py +160 -56
- fameio/input/scenario/exception.py +41 -18
- fameio/input/scenario/fameiofactory.py +57 -6
- fameio/input/scenario/generalproperties.py +22 -12
- fameio/input/scenario/scenario.py +117 -38
- fameio/input/scenario/stringset.py +29 -11
- fameio/input/schema/agenttype.py +27 -10
- fameio/input/schema/attribute.py +108 -45
- fameio/input/schema/java_packages.py +14 -12
- fameio/input/schema/schema.py +39 -15
- fameio/input/validator.py +198 -54
- fameio/input/writer.py +137 -46
- fameio/logs.py +28 -47
- fameio/output/__init__.py +5 -1
- fameio/output/agent_type.py +89 -28
- fameio/output/conversion.py +52 -37
- fameio/output/csv_writer.py +107 -27
- fameio/output/data_transformer.py +17 -24
- fameio/output/execution_dao.py +170 -0
- fameio/output/input_dao.py +71 -33
- fameio/output/output_dao.py +33 -11
- fameio/output/reader.py +64 -21
- fameio/output/yaml_writer.py +16 -8
- fameio/scripts/__init__.py +22 -4
- fameio/scripts/convert_results.py +126 -52
- fameio/scripts/convert_results.py.license +1 -1
- fameio/scripts/exception.py +7 -0
- fameio/scripts/make_config.py +34 -13
- fameio/scripts/make_config.py.license +1 -1
- fameio/scripts/reformat.py +71 -0
- fameio/scripts/reformat.py.license +3 -0
- fameio/series.py +174 -59
- fameio/time.py +79 -25
- fameio/tools.py +48 -8
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/METADATA +50 -34
- fameio-3.3.0.dist-info/RECORD +60 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/WHEEL +1 -1
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/entry_points.txt +1 -0
- CHANGELOG.md +0 -288
- fameio-3.1.1.dist-info/RECORD +0 -56
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSE.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
fameio/input/schema/attribute.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
|
6
6
|
from enum import Enum, auto
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import Any,
|
8
|
+
from typing import Any, Final
|
9
9
|
|
10
10
|
from fameio.input import SchemaError
|
11
11
|
from fameio.input.metadata import Metadata, ValueContainer
|
@@ -16,7 +16,7 @@ from fameio.tools import keys_to_lower
|
|
16
16
|
|
17
17
|
|
18
18
|
class AttributeType(Enum):
|
19
|
-
"""Data types that Attributes can take"""
|
19
|
+
"""Data types that Attributes can take."""
|
20
20
|
|
21
21
|
INTEGER = auto()
|
22
22
|
DOUBLE = auto()
|
@@ -28,7 +28,7 @@ class AttributeType(Enum):
|
|
28
28
|
TIME_SERIES = auto()
|
29
29
|
BLOCK = auto()
|
30
30
|
|
31
|
-
def convert_string_to_type(self, value: str) ->
|
31
|
+
def convert_string_to_type(self, value: str) -> int | float | str:
|
32
32
|
"""
|
33
33
|
Converts a given string to this AttributeType's data format
|
34
34
|
|
@@ -57,7 +57,7 @@ class AttributeType(Enum):
|
|
57
57
|
|
58
58
|
|
59
59
|
class AttributeSpecs(Metadata):
|
60
|
-
"""Schema Definition of a single Attribute (with possible inner Attributes) of an agent"""
|
60
|
+
"""Schema Definition of a single Attribute (with possible inner Attributes) of an agent."""
|
61
61
|
|
62
62
|
_DISALLOWED_NAMES = ["value", "values", "metadata"]
|
63
63
|
_SEPARATOR = "."
|
@@ -75,6 +75,7 @@ class AttributeSpecs(Metadata):
|
|
75
75
|
_MISSING_TYPE = "'AttributeType' not declare for Attribute '{}'."
|
76
76
|
_INVALID_TYPE = "'{}' is not a valid type for an Attribute."
|
77
77
|
_DEFAULT_NOT_LIST = "Attribute is list, but provided Default '{}' is not a list."
|
78
|
+
_DEFAULT_NOT_SIMPLE = "Only a simple Default value is allowed for non-list attributes, but was '{}'"
|
78
79
|
_INCOMPATIBLE = "Value '{}' in section '{}' can not be converted to AttributeType '{}'."
|
79
80
|
_DEFAULT_DISALLOWED = "Default '{}' is not an allowed value."
|
80
81
|
_SERIES_LIST_DISALLOWED = "Attribute '{}' of type TIME_SERIES cannot be a list."
|
@@ -82,13 +83,21 @@ class AttributeSpecs(Metadata):
|
|
82
83
|
_NAME_DISALLOWED = f"Attribute name must not be empty and none of: {_DISALLOWED_NAMES}"
|
83
84
|
|
84
85
|
def __init__(self, name: str, definition: dict):
|
85
|
-
"""Loads Attribute from given `definition
|
86
|
+
"""Loads Attribute from given `definition`.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
name: of attribute type
|
90
|
+
definition: of attribute type
|
91
|
+
|
92
|
+
Raises:
|
93
|
+
SchemaError: if attribute type is not properly defined, logged with level "ERROR"
|
94
|
+
"""
|
86
95
|
super().__init__(definition)
|
87
96
|
self._assert_is_allowed_name(name)
|
88
97
|
self._full_name = name
|
89
98
|
|
90
99
|
if not definition:
|
91
|
-
raise SchemaError(AttributeSpecs._EMPTY_DEFINITION.format(name))
|
100
|
+
raise log_error(SchemaError(AttributeSpecs._EMPTY_DEFINITION.format(name)))
|
92
101
|
definition = keys_to_lower(definition)
|
93
102
|
|
94
103
|
self._is_mandatory = self._get_is_mandatory(definition, name)
|
@@ -96,7 +105,7 @@ class AttributeSpecs(Metadata):
|
|
96
105
|
self._attr_type = self._get_type(definition, name)
|
97
106
|
|
98
107
|
if self._attr_type == AttributeType.TIME_SERIES and self._is_list:
|
99
|
-
raise SchemaError(AttributeSpecs._SERIES_LIST_DISALLOWED.format(name))
|
108
|
+
raise log_error(SchemaError(AttributeSpecs._SERIES_LIST_DISALLOWED.format(name)))
|
100
109
|
|
101
110
|
self._allowed_values = self._get_allowed_values(definition)
|
102
111
|
self._default_value = self._get_default_value(definition)
|
@@ -105,18 +114,25 @@ class AttributeSpecs(Metadata):
|
|
105
114
|
|
106
115
|
@staticmethod
|
107
116
|
def _assert_is_allowed_name(full_name: str) -> None:
|
108
|
-
"""Raises SchemaError if provided name is not allowed for Attributes
|
117
|
+
"""Raises SchemaError if provided name is not allowed for Attributes.
|
118
|
+
|
119
|
+
Args:
|
120
|
+
full_name: to be checked if it can serve as name for an attribute
|
121
|
+
|
122
|
+
Raises:
|
123
|
+
SchemaError: if name is not allowed, logged with level "ERROR"
|
124
|
+
"""
|
109
125
|
if full_name is None:
|
110
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
126
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
111
127
|
short_name = full_name.split(AttributeSpecs._SEPARATOR)[-1]
|
112
128
|
if len(short_name) == 0 or short_name.isspace():
|
113
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
129
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
114
130
|
if short_name.lower() in AttributeSpecs._DISALLOWED_NAMES:
|
115
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
131
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
116
132
|
|
117
133
|
@staticmethod
|
118
134
|
def _get_is_mandatory(definition: dict, name: str) -> bool:
|
119
|
-
"""Returns True if `Mandatory` is set to True or if specification is missing; False otherwise"""
|
135
|
+
"""Returns True if `Mandatory` is set to True or if specification is missing; False otherwise."""
|
120
136
|
if AttributeSpecs.KEY_MANDATORY in definition:
|
121
137
|
return definition[AttributeSpecs.KEY_MANDATORY]
|
122
138
|
log().warning(AttributeSpecs._MISSING_SPEC_DEFAULT.format(AttributeSpecs.KEY_MANDATORY, name, True))
|
@@ -124,7 +140,7 @@ class AttributeSpecs(Metadata):
|
|
124
140
|
|
125
141
|
@staticmethod
|
126
142
|
def _get_is_list(definition: dict, name: str) -> bool:
|
127
|
-
"""Returns True if `List` is set to True; Returns False otherwise or if specification is missing"""
|
143
|
+
"""Returns True if `List` is set to True; Returns False otherwise or if specification is missing."""
|
128
144
|
if AttributeSpecs.KEY_LIST in definition:
|
129
145
|
return definition[AttributeSpecs.KEY_LIST]
|
130
146
|
log().warning(AttributeSpecs._MISSING_SPEC_DEFAULT.format(AttributeSpecs.KEY_LIST, name, False))
|
@@ -132,17 +148,28 @@ class AttributeSpecs(Metadata):
|
|
132
148
|
|
133
149
|
@staticmethod
|
134
150
|
def _get_type(definition: dict, name: str) -> AttributeType:
|
135
|
-
"""Returns `AttributeType` from given definition
|
151
|
+
"""Returns `AttributeType` from given definition.
|
152
|
+
|
153
|
+
Args:
|
154
|
+
definition: of the attribute
|
155
|
+
name: of the attribute
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
type of attribute
|
159
|
+
|
160
|
+
Raises:
|
161
|
+
SchemaError: if no proper type can be extracted, logged with level "ERROR"
|
162
|
+
"""
|
136
163
|
if AttributeSpecs.KEY_TYPE in definition:
|
137
164
|
type_name = definition[AttributeSpecs.KEY_TYPE]
|
138
165
|
try:
|
139
166
|
return AttributeType[type_name.upper()]
|
140
167
|
except KeyError as e:
|
141
|
-
raise SchemaError(AttributeSpecs._INVALID_TYPE.format(type_name)) from e
|
168
|
+
raise log_error(SchemaError(AttributeSpecs._INVALID_TYPE.format(type_name))) from e
|
142
169
|
raise log_error(SchemaError(AttributeSpecs._MISSING_TYPE.format(name)))
|
143
170
|
|
144
171
|
def _get_allowed_values(self, definition: dict) -> ValueContainer:
|
145
|
-
"""Returns ValueContainer with allowed values if defined; otherwise an empty ValueContainer"""
|
172
|
+
"""Returns ValueContainer with allowed values if defined; otherwise an empty ValueContainer."""
|
146
173
|
allowed_values: ValueContainer = ValueContainer()
|
147
174
|
if AttributeSpecs.KEY_VALUES in definition:
|
148
175
|
value_definition = definition[AttributeSpecs.KEY_VALUES]
|
@@ -150,16 +177,19 @@ class AttributeSpecs(Metadata):
|
|
150
177
|
allowed_values = self._read_values(value_definition)
|
151
178
|
return allowed_values
|
152
179
|
|
153
|
-
def _read_values(self, definition:
|
154
|
-
"""
|
155
|
-
|
156
|
-
Accepts lists of
|
180
|
+
def _read_values(self, definition: dict | list) -> ValueContainer:
|
181
|
+
"""Returns acceptable values mapped to their optional metadata specifications extracted from given `definition`.
|
182
|
+
|
183
|
+
Accepts lists of values or dictionaries with (optional) metadata assigned to each value
|
157
184
|
|
158
185
|
Args:
|
159
186
|
definition: list of acceptable values or dict with acceptable values as keys and (optional) metadata content
|
160
187
|
|
161
188
|
Returns:
|
162
189
|
Mapping of acceptable values to their associated Metadata
|
190
|
+
|
191
|
+
Raises:
|
192
|
+
SchemaError: if values could not be parsed or do not match the attribute type, logged with level "ERROR"
|
163
193
|
"""
|
164
194
|
try:
|
165
195
|
value_container = ValueContainer(definition)
|
@@ -167,17 +197,28 @@ class AttributeSpecs(Metadata):
|
|
167
197
|
self._convert_to_data_type(value, self.KEY_VALUES)
|
168
198
|
return value_container
|
169
199
|
except ValueContainer.ParseError as e:
|
170
|
-
raise SchemaError(AttributeSpecs._VALUES_ILL_FORMAT.format(definition)) from e
|
200
|
+
raise log_error(SchemaError(AttributeSpecs._VALUES_ILL_FORMAT.format(definition))) from e
|
171
201
|
|
172
|
-
def _convert_to_data_type(self, value: str, section: str) ->
|
173
|
-
"""Returns a given single `value` in `section` converted to this Attribute's data type
|
202
|
+
def _convert_to_data_type(self, value: str, section: str) -> int | float | str:
|
203
|
+
"""Returns a given single `value` in `section` converted to this Attribute's data type.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
value: to be converted
|
207
|
+
section: that contains the value
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
value with type matching this attribute type
|
211
|
+
|
212
|
+
Raises:
|
213
|
+
SchemaError: if value does not match this type of attribute, logged with level "ERROR"
|
214
|
+
"""
|
174
215
|
try:
|
175
216
|
return self._attr_type.convert_string_to_type(value)
|
176
217
|
except ValueError as e:
|
177
|
-
raise SchemaError(AttributeSpecs._INCOMPATIBLE.format(value, section, self._attr_type)) from e
|
218
|
+
raise log_error(SchemaError(AttributeSpecs._INCOMPATIBLE.format(value, section, self._attr_type))) from e
|
178
219
|
|
179
|
-
def _get_default_value(self, definition: dict) ->
|
180
|
-
"""Returns default value(s) from given definitions, or None if no default is specified"""
|
220
|
+
def _get_default_value(self, definition: dict) -> int | float | str | list | None:
|
221
|
+
"""Returns default value(s) from given definitions, or None if no default is specified."""
|
181
222
|
if AttributeSpecs.KEY_DEFAULT in definition:
|
182
223
|
provided_value = definition[AttributeSpecs.KEY_DEFAULT]
|
183
224
|
if self._is_list:
|
@@ -186,20 +227,42 @@ class AttributeSpecs(Metadata):
|
|
186
227
|
return None
|
187
228
|
|
188
229
|
def _convert_list(self, values) -> list:
|
189
|
-
"""Converts all entries in given `values` list to this attribute data type and returns this new list
|
230
|
+
"""Converts all entries in given `values` list to this attribute data type and returns this new list.
|
231
|
+
|
232
|
+
Args:
|
233
|
+
values: to be converted to a list of default values
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
default values
|
237
|
+
|
238
|
+
Raises:
|
239
|
+
SchemaError: if provided default is not a list, logged with level "ERROR"
|
240
|
+
"""
|
190
241
|
if isinstance(values, list):
|
191
242
|
return [self._convert_and_test(item) for item in values]
|
192
|
-
raise SchemaError(AttributeSpecs._DEFAULT_NOT_LIST.format(values))
|
243
|
+
raise log_error(SchemaError(AttributeSpecs._DEFAULT_NOT_LIST.format(values)))
|
193
244
|
|
194
245
|
def _convert_and_test(self, value: str):
|
195
|
-
"""Converts a given single `value` to this Attribute's data type and tests if the value is allowed
|
246
|
+
"""Converts a given single `value` to this Attribute's data type and tests if the value is allowed.
|
247
|
+
|
248
|
+
Args:
|
249
|
+
value: to be converted and tested
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
the value converted to the data type matching this attribute type
|
253
|
+
|
254
|
+
Raises:
|
255
|
+
SchemaError: if the provided default could not be converted or is not allowed, logged with level "ERROR"
|
256
|
+
"""
|
257
|
+
if isinstance(value, (list, dict)):
|
258
|
+
raise log_error(SchemaError(self._DEFAULT_NOT_SIMPLE.format(value)))
|
196
259
|
if self.has_value_restrictions and (not self._allowed_values.has_value(value)):
|
197
|
-
raise SchemaError(AttributeSpecs._DEFAULT_DISALLOWED.format(value))
|
260
|
+
raise log_error(SchemaError(AttributeSpecs._DEFAULT_DISALLOWED.format(value)))
|
198
261
|
return self._convert_to_data_type(value, self.KEY_DEFAULT)
|
199
262
|
|
200
263
|
@staticmethod
|
201
264
|
def _get_nested_attributes(definition: dict, name: str) -> dict[str, AttributeSpecs]:
|
202
|
-
"""Returns dict of nested attributes read from given definition; empty dict if no nested attributes exist"""
|
265
|
+
"""Returns dict of nested attributes read from given definition; empty dict if no nested attributes exist."""
|
203
266
|
nested_attributes = {}
|
204
267
|
if AttributeSpecs.KEY_NESTED in definition:
|
205
268
|
for nested_name, nested_details in definition[AttributeSpecs.KEY_NESTED].items():
|
@@ -209,67 +272,67 @@ class AttributeSpecs(Metadata):
|
|
209
272
|
|
210
273
|
@staticmethod
|
211
274
|
def _get_help(definition) -> str:
|
212
|
-
"""Returns (possible empty) help text if provided in definition; None otherwise"""
|
275
|
+
"""Returns (possible empty) help text if provided in definition; None otherwise."""
|
213
276
|
return definition.get(AttributeSpecs.KEY_HELP, "").strip()
|
214
277
|
|
215
278
|
@property
|
216
279
|
def attr_type(self) -> AttributeType:
|
217
|
-
"""Returns AttributeType of this attribute"""
|
280
|
+
"""Returns AttributeType of this attribute."""
|
218
281
|
return self._attr_type
|
219
282
|
|
220
283
|
@property
|
221
284
|
def values(self) -> list:
|
222
|
-
"""Returns the list of allowed values for this attribute"""
|
285
|
+
"""Returns the list of allowed values for this attribute."""
|
223
286
|
return self._allowed_values.as_list()
|
224
287
|
|
225
288
|
@property
|
226
289
|
def has_value_restrictions(self) -> bool:
|
227
|
-
"""Returns True if the attribute can only take a set of certain values"""
|
290
|
+
"""Returns True if the attribute can only take a set of certain values."""
|
228
291
|
return not self._allowed_values.is_empty()
|
229
292
|
|
230
293
|
@property
|
231
294
|
def is_list(self) -> bool:
|
232
|
-
"""Return True if this attribute type is a list"""
|
295
|
+
"""Return True if this attribute type is a list."""
|
233
296
|
return self._is_list
|
234
297
|
|
235
298
|
@property
|
236
299
|
def has_nested_attributes(self) -> bool:
|
237
|
-
"""Returns True if nested attributes are defined"""
|
300
|
+
"""Returns True if nested attributes are defined."""
|
238
301
|
return bool(self._nested_attributes)
|
239
302
|
|
240
303
|
@property
|
241
304
|
def nested_attributes(self) -> dict[str, AttributeSpecs]:
|
242
|
-
"""Returns list of nested Attributes of this Attribute or an empty dict if no nested attributes are defined"""
|
305
|
+
"""Returns list of nested Attributes of this Attribute or an empty dict if no nested attributes are defined."""
|
243
306
|
return self._nested_attributes
|
244
307
|
|
245
308
|
@property
|
246
309
|
def has_default_value(self) -> bool:
|
247
|
-
"""Return True if a default value is available"""
|
310
|
+
"""Return True if a default value is available."""
|
248
311
|
return self._default_value is not None
|
249
312
|
|
250
313
|
@property
|
251
|
-
def default_value(self) ->
|
252
|
-
"""Return the default value of this attribute, or None if no default is specified"""
|
314
|
+
def default_value(self) -> Any | None:
|
315
|
+
"""Return the default value of this attribute, or None if no default is specified."""
|
253
316
|
return self._default_value
|
254
317
|
|
255
318
|
@property
|
256
319
|
def is_mandatory(self) -> bool:
|
257
|
-
"""Return True if this attribute is mandatory"""
|
320
|
+
"""Return True if this attribute is mandatory."""
|
258
321
|
return self._is_mandatory
|
259
322
|
|
260
323
|
@property
|
261
324
|
def full_name(self) -> str:
|
262
|
-
"""Returns name including name of enclosing parent attributes"""
|
325
|
+
"""Returns name including name of enclosing parent attributes."""
|
263
326
|
return self._full_name
|
264
327
|
|
265
328
|
@property
|
266
329
|
def has_help_text(self) -> bool:
|
267
|
-
"""Return True if a help_text is available"""
|
330
|
+
"""Return True if a help_text is available."""
|
268
331
|
return bool(self._help)
|
269
332
|
|
270
333
|
@property
|
271
334
|
def help_text(self) -> str:
|
272
|
-
"""Return the help_text of this attribute, if any"""
|
335
|
+
"""Return the help_text of this attribute, if any."""
|
273
336
|
return self._help
|
274
337
|
|
275
338
|
def _to_dict(self) -> dict[str, Any]:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# SPDX-FileCopyrightText:
|
1
|
+
# SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
from __future__ import annotations
|
@@ -6,12 +6,12 @@ from __future__ import annotations
|
|
6
6
|
from typing import Final
|
7
7
|
|
8
8
|
from fameio.input import SchemaError
|
9
|
-
from fameio.logs import
|
9
|
+
from fameio.logs import log, log_error
|
10
10
|
from fameio.tools import keys_to_lower
|
11
11
|
|
12
12
|
|
13
13
|
class JavaPackages:
|
14
|
-
"""Schema definitions for Java package names in which model classes reside"""
|
14
|
+
"""Schema definitions for Java package names in which model classes reside."""
|
15
15
|
|
16
16
|
KEY_AGENT: Final[str] = "Agents".lower()
|
17
17
|
KEY_DATA_ITEM: Final[str] = "DataItems".lower()
|
@@ -21,21 +21,23 @@ class JavaPackages:
|
|
21
21
|
_INFO_MISSING_DATA_ITEMS = "`DataItems` not specified: Key was missing or list was empty."
|
22
22
|
_ERR_MISSING_PORTABLES = "JavaPackages require non-empty list for `Portables`. Key was missing or list was empty."
|
23
23
|
|
24
|
-
def __init__(self):
|
24
|
+
def __init__(self) -> None:
|
25
25
|
self._agents: list[str] = []
|
26
26
|
self._data_items: list[str] = []
|
27
27
|
self._portables: list[str] = []
|
28
28
|
|
29
29
|
@classmethod
|
30
30
|
def from_dict(cls, definitions: dict[str, list[str]]) -> JavaPackages:
|
31
|
-
"""
|
32
|
-
Creates JavaPackages from a dictionary representation
|
31
|
+
"""Creates JavaPackages from a dictionary representation.
|
33
32
|
|
34
33
|
Args:
|
35
34
|
definitions: dictionary representation of JavaPackages
|
36
35
|
|
37
36
|
Returns:
|
38
37
|
new instance of JavaPackages
|
38
|
+
|
39
|
+
Raises:
|
40
|
+
SchemaError: if definitions are incomplete or erroneous, logged on level "ERROR"
|
39
41
|
"""
|
40
42
|
java_packages = cls()
|
41
43
|
definitions = keys_to_lower(definitions)
|
@@ -45,29 +47,29 @@ class JavaPackages:
|
|
45
47
|
java_packages._portables = definitions.get(JavaPackages.KEY_PORTABLE, [])
|
46
48
|
|
47
49
|
if not java_packages._agents:
|
48
|
-
|
50
|
+
raise log_error(SchemaError(JavaPackages._ERR_MISSING_AGENTS))
|
49
51
|
if not java_packages._data_items:
|
50
52
|
log().info(JavaPackages._INFO_MISSING_DATA_ITEMS)
|
51
53
|
if not java_packages._portables:
|
52
|
-
|
54
|
+
raise log_error(SchemaError(JavaPackages._ERR_MISSING_PORTABLES))
|
53
55
|
|
54
56
|
return java_packages
|
55
57
|
|
56
58
|
@property
|
57
59
|
def agents(self) -> list[str]:
|
58
|
-
"""Return list of java package names that contain the model's Agents"""
|
60
|
+
"""Return list of java package names that contain the model's Agents."""
|
59
61
|
return self._agents
|
60
62
|
|
61
63
|
@property
|
62
64
|
def data_items(self) -> list[str]:
|
63
|
-
"""Return list of java package names that contain the model's DataItems"""
|
65
|
+
"""Return list of java package names that contain the model's DataItems."""
|
64
66
|
return self._data_items
|
65
67
|
|
66
68
|
@property
|
67
69
|
def portables(self) -> list[str]:
|
68
|
-
"""Return list of java package names that contain the model's Portables"""
|
70
|
+
"""Return list of java package names that contain the model's Portables."""
|
69
71
|
return self._portables
|
70
72
|
|
71
73
|
def to_dict(self) -> dict[str, list[str]]:
|
72
|
-
"""Return dictionary representation of this JavaPackages object"""
|
74
|
+
"""Return dictionary representation of this JavaPackages object."""
|
73
75
|
return {self.KEY_AGENT: self.agents, self.KEY_DATA_ITEM: self.data_items, self.KEY_PORTABLE: self.portables}
|
fameio/input/schema/schema.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SPDX-FileCopyrightText:
|
1
|
+
# SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
from __future__ import annotations
|
@@ -7,14 +7,14 @@ import ast
|
|
7
7
|
from typing import Any, Final
|
8
8
|
|
9
9
|
from fameio.input import SchemaError
|
10
|
-
from fameio.logs import
|
10
|
+
from fameio.logs import log_error
|
11
11
|
from fameio.tools import keys_to_lower
|
12
12
|
from .agenttype import AgentType
|
13
13
|
from .java_packages import JavaPackages
|
14
14
|
|
15
15
|
|
16
16
|
class Schema:
|
17
|
-
"""Definition of a schema"""
|
17
|
+
"""Definition of a schema."""
|
18
18
|
|
19
19
|
KEY_AGENT_TYPE: Final[str] = "AgentTypes".lower()
|
20
20
|
KEY_PACKAGES: Final[str] = "JavaPackages".lower()
|
@@ -23,20 +23,30 @@ class Schema:
|
|
23
23
|
_ERR_AGENT_TYPES_EMPTY = "`AgentTypes` must not be empty - at least one type of agent is required."
|
24
24
|
_ERR_MISSING_PACKAGES = "Missing required section `JavaPackages` in Schema."
|
25
25
|
|
26
|
-
def __init__(self, definitions: dict):
|
26
|
+
def __init__(self, definitions: dict) -> None:
|
27
27
|
self._original_input_dict = definitions
|
28
|
-
self._agent_types = {}
|
29
|
-
self._packages = None
|
28
|
+
self._agent_types: dict[str, AgentType] = {}
|
29
|
+
self._packages: JavaPackages | None = None
|
30
30
|
|
31
31
|
@classmethod
|
32
32
|
def from_dict(cls, definitions: dict) -> Schema:
|
33
|
-
"""
|
33
|
+
"""Convert given `definitions` into a new schema.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
definitions: dictionary representation of schema
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
schema created from given definitions
|
40
|
+
|
41
|
+
Raises:
|
42
|
+
SchemaError: if definitions are incomplete or erroneous, logged on level "ERROR"
|
43
|
+
"""
|
34
44
|
definitions = keys_to_lower(definitions)
|
35
45
|
schema = cls(definitions)
|
36
46
|
|
37
47
|
agent_types = cls._get_or_raise(definitions, Schema.KEY_AGENT_TYPE, Schema._ERR_AGENT_TYPES_MISSING)
|
38
48
|
if len(agent_types) == 0:
|
39
|
-
|
49
|
+
raise log_error(SchemaError(Schema._ERR_AGENT_TYPES_EMPTY))
|
40
50
|
for agent_type_name, agent_definition in agent_types.items():
|
41
51
|
agent_type = AgentType.from_dict(agent_type_name, agent_definition)
|
42
52
|
schema._agent_types[agent_type_name] = agent_type
|
@@ -48,30 +58,44 @@ class Schema:
|
|
48
58
|
|
49
59
|
@staticmethod
|
50
60
|
def _get_or_raise(definitions: dict[str, Any], key: str, error_message: str) -> Any:
|
51
|
-
"""Get given `key` from given `definitions` - raise error with given `error_message` if not present
|
61
|
+
"""Get given `key` from given `definitions` - raise error with given `error_message` if not present.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
definitions: to search the key in
|
65
|
+
key: to be searched
|
66
|
+
error_message: to be logged and included in the raised exception if key is missing
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
value associated with given key in given definitions
|
70
|
+
|
71
|
+
Raises:
|
72
|
+
SchemaError: if given key is not in given definitions, logged on level "ERROR"
|
73
|
+
"""
|
52
74
|
if key not in definitions:
|
53
|
-
|
75
|
+
raise log_error(SchemaError(error_message))
|
54
76
|
return definitions[key]
|
55
77
|
|
56
78
|
@classmethod
|
57
79
|
def from_string(cls, definitions: str) -> Schema:
|
58
|
-
"""Load given string `definitions` into a new Schema"""
|
80
|
+
"""Load given string `definitions` into a new Schema."""
|
59
81
|
return cls.from_dict(ast.literal_eval(definitions))
|
60
82
|
|
61
83
|
def to_dict(self) -> dict:
|
62
|
-
"""Serializes the schema content to a dict"""
|
84
|
+
"""Serializes the schema content to a dict."""
|
63
85
|
return self._original_input_dict
|
64
86
|
|
65
87
|
def to_string(self) -> str:
|
66
|
-
"""Returns a string representation of the Schema of which the class can be rebuilt"""
|
88
|
+
"""Returns a string representation of the Schema of which the class can be rebuilt."""
|
67
89
|
return repr(self.to_dict())
|
68
90
|
|
69
91
|
@property
|
70
92
|
def agent_types(self) -> dict[str, AgentType]:
|
71
|
-
"""Returns all the agent types by their name"""
|
93
|
+
"""Returns all the agent types by their name."""
|
72
94
|
return self._agent_types
|
73
95
|
|
74
96
|
@property
|
75
97
|
def packages(self) -> JavaPackages:
|
76
|
-
"""Returns JavaPackages, i.e. names where model classes are defined in"""
|
98
|
+
"""Returns JavaPackages, i.e. names where model classes are defined in."""
|
99
|
+
if self._packages is None:
|
100
|
+
raise log_error(SchemaError(self._ERR_MISSING_PACKAGES))
|
77
101
|
return self._packages
|