fameio 3.1.1__py3-none-any.whl → 3.2.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 +6 -4
- fameio/cli/make_config.py +6 -4
- fameio/cli/parser.py +41 -29
- fameio/input/__init__.py +1 -1
- fameio/input/loader/__init__.py +9 -7
- fameio/input/loader/controller.py +59 -8
- fameio/input/loader/loader.py +14 -7
- fameio/input/metadata.py +35 -13
- fameio/input/resolver.py +5 -4
- fameio/input/scenario/agent.py +50 -16
- fameio/input/scenario/attribute.py +14 -15
- fameio/input/scenario/contract.py +152 -43
- fameio/input/scenario/exception.py +44 -18
- fameio/input/scenario/fameiofactory.py +63 -7
- fameio/input/scenario/generalproperties.py +17 -6
- fameio/input/scenario/scenario.py +111 -28
- fameio/input/scenario/stringset.py +27 -8
- fameio/input/schema/agenttype.py +21 -2
- fameio/input/schema/attribute.py +91 -22
- fameio/input/schema/java_packages.py +8 -5
- fameio/input/schema/schema.py +35 -9
- fameio/input/validator.py +22 -15
- fameio/input/writer.py +136 -36
- fameio/logs.py +3 -31
- fameio/output/__init__.py +5 -1
- fameio/output/agent_type.py +86 -23
- fameio/output/conversion.py +47 -29
- fameio/output/csv_writer.py +88 -18
- fameio/output/data_transformer.py +7 -14
- fameio/output/input_dao.py +62 -21
- fameio/output/output_dao.py +26 -4
- fameio/output/reader.py +58 -13
- fameio/output/yaml_writer.py +15 -6
- fameio/scripts/__init__.py +9 -2
- fameio/scripts/convert_results.py +123 -50
- fameio/scripts/convert_results.py.license +1 -1
- fameio/scripts/exception.py +7 -0
- fameio/scripts/make_config.py +34 -12
- fameio/scripts/make_config.py.license +1 -1
- fameio/series.py +117 -33
- fameio/time.py +74 -17
- fameio/tools.py +7 -5
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/METADATA +19 -13
- fameio-3.2.0.dist-info/RECORD +56 -0
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/WHEEL +1 -1
- CHANGELOG.md +0 -288
- fameio-3.1.1.dist-info/RECORD +0 -56
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSE.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/entry_points.txt +0 -0
@@ -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
|
@@ -8,7 +8,7 @@ from typing import Final
|
|
8
8
|
from fameio.logs import log
|
9
9
|
from fameio.time import FameTime
|
10
10
|
from fameio.tools import keys_to_lower
|
11
|
-
from .exception import
|
11
|
+
from .exception import get_or_raise
|
12
12
|
|
13
13
|
|
14
14
|
class GeneralProperties:
|
@@ -39,9 +39,20 @@ class GeneralProperties:
|
|
39
39
|
|
40
40
|
@classmethod
|
41
41
|
def from_dict(cls, definitions: dict) -> GeneralProperties:
|
42
|
-
"""
|
42
|
+
"""
|
43
|
+
Parse general properties from provided `definitions`
|
44
|
+
|
45
|
+
Args:
|
46
|
+
definitions: dictionary representation of general properties
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
new GeneralProperties
|
50
|
+
|
51
|
+
Raises:
|
52
|
+
ScenarioError: if definitions are incomplete or erroneous, logged on level "ERROR"
|
53
|
+
"""
|
43
54
|
definitions = keys_to_lower(definitions)
|
44
|
-
run_id =
|
55
|
+
run_id = definitions.get(GeneralProperties.KEY_RUN, 1)
|
45
56
|
|
46
57
|
simulation_definition = keys_to_lower(
|
47
58
|
get_or_raise(
|
@@ -64,12 +75,12 @@ class GeneralProperties:
|
|
64
75
|
GeneralProperties._ERR_MISSING_KEY,
|
65
76
|
)
|
66
77
|
)
|
67
|
-
random_seed =
|
78
|
+
random_seed = simulation_definition.get(GeneralProperties.KEY_SEED, 1)
|
68
79
|
return cls(run_id, start_time, stop_time, random_seed)
|
69
80
|
|
70
81
|
def to_dict(self) -> dict:
|
71
82
|
"""Serializes the general properties to a dict"""
|
72
|
-
result = {self.KEY_RUN: self._run_id}
|
83
|
+
result: dict = {self.KEY_RUN: self._run_id}
|
73
84
|
simulation_dict = {
|
74
85
|
self.KEY_START: self.simulation_start_time,
|
75
86
|
self.KEY_STOP: self.simulation_stop_time,
|
@@ -1,19 +1,20 @@
|
|
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
|
5
5
|
|
6
|
-
from typing import Final
|
6
|
+
from typing import Final, Any
|
7
7
|
|
8
|
+
from fameio.input import SchemaError
|
8
9
|
from fameio.input.metadata import Metadata
|
10
|
+
from fameio.input.scenario.agent import Agent
|
11
|
+
from fameio.input.scenario.contract import Contract
|
12
|
+
from fameio.input.scenario.exception import get_or_raise, log_scenario_error
|
13
|
+
from fameio.input.scenario.fameiofactory import FameIOFactory
|
14
|
+
from fameio.input.scenario.generalproperties import GeneralProperties
|
15
|
+
from fameio.input.scenario.stringset import StringSet
|
9
16
|
from fameio.input.schema import Schema
|
10
17
|
from fameio.tools import keys_to_lower
|
11
|
-
from .agent import Agent
|
12
|
-
from .contract import Contract
|
13
|
-
from .exception import get_or_default, get_or_raise
|
14
|
-
from .fameiofactory import FameIOFactory
|
15
|
-
from .generalproperties import GeneralProperties
|
16
|
-
from .stringset import StringSet
|
17
18
|
|
18
19
|
|
19
20
|
class Scenario(Metadata):
|
@@ -26,42 +27,124 @@ class Scenario(Metadata):
|
|
26
27
|
KEY_STRING_SETS: Final[str] = "StringSets".lower()
|
27
28
|
|
28
29
|
_MISSING_KEY = "Scenario definition misses required key '{}'."
|
30
|
+
_ERR_SCHEMA = "Could not create scenario: Definition of Schema has errors."
|
31
|
+
_ERR_STRING_SET = "Could not create scenario: Definition of StringSet '{}' has errors."
|
32
|
+
_ERR_MULTI_CONTRACT = "Could not create scenario: Definition of Contracts has errors: {}"
|
33
|
+
_ERR_CONTRACT = "Could not create scenario: Definition of Contract has errors: {}"
|
29
34
|
|
30
35
|
def __init__(self, schema: Schema, general_props: GeneralProperties) -> None:
|
31
36
|
super().__init__()
|
32
37
|
self._schema = schema
|
33
38
|
self._general_props = general_props
|
34
|
-
self._string_sets = {}
|
35
|
-
self._agents = []
|
36
|
-
self._contracts = []
|
39
|
+
self._string_sets: dict[str, StringSet] = {}
|
40
|
+
self._agents: list[Agent] = []
|
41
|
+
self._contracts: list[Contract] = []
|
37
42
|
|
38
43
|
@classmethod
|
39
|
-
def from_dict(cls, definitions: dict, factory=FameIOFactory()) -> Scenario:
|
40
|
-
"""
|
41
|
-
definitions
|
44
|
+
def from_dict(cls, definitions: dict, factory: FameIOFactory = FameIOFactory()) -> Scenario:
|
45
|
+
"""
|
46
|
+
Parse scenario from provided `definitions` using given `factory`
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
)
|
47
|
-
scenario = cls(schema, general_props)
|
48
|
-
scenario._extract_metadata(definitions)
|
48
|
+
Args:
|
49
|
+
definitions: dictionary representation of scenario
|
50
|
+
factory: helper class with static helpers to instantiate scenario components
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
Returns:
|
53
|
+
new Scenario
|
52
54
|
|
53
|
-
|
54
|
-
scenario
|
55
|
+
Raises:
|
56
|
+
ScenarioError: if scenario definitions are incomplete or erroneous, logged with level "ERROR"
|
57
|
+
"""
|
58
|
+
definitions = keys_to_lower(definitions)
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
60
|
+
schema = Scenario._extract_schema(definitions, factory)
|
61
|
+
general_properties_definition = get_or_raise(definitions, Scenario.KEY_GENERAL, Scenario._MISSING_KEY)
|
62
|
+
general_properties = factory.new_general_properties_from_dict(general_properties_definition)
|
63
|
+
scenario = cls(schema, general_properties)
|
64
|
+
scenario._extract_metadata(definitions)
|
59
65
|
|
66
|
+
scenario._string_sets = Scenario._extract_string_sets(definitions, factory)
|
67
|
+
scenario._agents = [
|
68
|
+
factory.new_agent_from_dict(agent_definition)
|
69
|
+
for agent_definition in definitions.get(Scenario.KEY_AGENTS, [])
|
70
|
+
]
|
71
|
+
scenario._contracts = Scenario._extract_contracts(definitions, factory)
|
60
72
|
return scenario
|
61
73
|
|
74
|
+
@staticmethod
|
75
|
+
def _extract_schema(definitions: dict, factory: FameIOFactory) -> Schema:
|
76
|
+
"""
|
77
|
+
Extract schema from given definitions and create Schema from it
|
78
|
+
|
79
|
+
Args:
|
80
|
+
definitions: dictionary representation of scenario
|
81
|
+
factory: helper class with static helpers to instantiate scenario components
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
new schema
|
85
|
+
|
86
|
+
Raises:
|
87
|
+
ScenarioError: if schema definitions are missing, incomplete, or erroneous; logged on level "ERROR"
|
88
|
+
"""
|
89
|
+
schema_definition = get_or_raise(definitions, Scenario.KEY_SCHEMA, Scenario._MISSING_KEY)
|
90
|
+
try:
|
91
|
+
return factory.new_schema_from_dict(schema_definition)
|
92
|
+
except SchemaError as e:
|
93
|
+
raise log_scenario_error(Scenario._ERR_SCHEMA) from e
|
94
|
+
|
95
|
+
@staticmethod
|
96
|
+
def _extract_string_sets(definitions: dict, factory: FameIOFactory) -> dict[str, StringSet]:
|
97
|
+
"""
|
98
|
+
Extract string sets from given definitions and create dictionary from it
|
99
|
+
|
100
|
+
Args:
|
101
|
+
definitions: dictionary representation of scenario
|
102
|
+
factory: helper class with static helpers to instantiate scenario components
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
dictionary of string set names associated with their corresponding string set
|
106
|
+
|
107
|
+
Raises:
|
108
|
+
ScenarioError: if string set definitions are incomplete or erroneous; logged on level "ERROR"
|
109
|
+
"""
|
110
|
+
string_sets = {}
|
111
|
+
for name, string_set_definition in definitions.get(Scenario.KEY_STRING_SETS, {}).items():
|
112
|
+
try:
|
113
|
+
string_sets[name] = factory.new_string_set_from_dict(string_set_definition)
|
114
|
+
except StringSet.StringSetError as e:
|
115
|
+
raise log_scenario_error(Scenario._ERR_STRING_SET.format(name)) from e
|
116
|
+
return string_sets
|
117
|
+
|
118
|
+
@staticmethod
|
119
|
+
def _extract_contracts(definitions: dict, factory: FameIOFactory) -> list[Contract]:
|
120
|
+
"""
|
121
|
+
Extract contracts from given definitions
|
122
|
+
|
123
|
+
Args:
|
124
|
+
definitions: dictionary representation of scenario
|
125
|
+
factory: helper class with static helpers to instantiate scenario components
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
list of all created one-to-one contracts
|
129
|
+
|
130
|
+
Raises:
|
131
|
+
ScenarioError: if contract definitions are incomplete or erroneous; logged on level "ERROR"
|
132
|
+
"""
|
133
|
+
contracts = []
|
134
|
+
for multi_contract_definition in definitions.get(Scenario.KEY_CONTRACTS, []):
|
135
|
+
try:
|
136
|
+
for single_contract_definition in Contract.split_contract_definitions(multi_contract_definition):
|
137
|
+
try:
|
138
|
+
contracts.append(factory.new_contract_from_dict(single_contract_definition))
|
139
|
+
except Contract.ContractError as e:
|
140
|
+
raise log_scenario_error(Scenario._ERR_CONTRACT.format(single_contract_definition)) from e
|
141
|
+
except Contract.ContractError as e:
|
142
|
+
raise log_scenario_error(Scenario._ERR_MULTI_CONTRACT.format(multi_contract_definition)) from e
|
143
|
+
return contracts
|
144
|
+
|
62
145
|
def _to_dict(self) -> dict:
|
63
146
|
"""Serializes the scenario content to a dict"""
|
64
|
-
result = {
|
147
|
+
result: dict[str, Any] = {
|
65
148
|
Scenario.KEY_GENERAL: self.general_properties.to_dict(),
|
66
149
|
Scenario.KEY_SCHEMA: self.schema.to_dict(),
|
67
150
|
}
|
@@ -1,38 +1,57 @@
|
|
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
|
5
5
|
|
6
|
-
from typing import
|
6
|
+
from typing import Final, Any, Union
|
7
7
|
|
8
|
+
from fameio.input import InputError
|
8
9
|
from fameio.input.metadata import Metadata, MetadataComponent, ValueContainer
|
10
|
+
from fameio.logs import log_error
|
9
11
|
from fameio.tools import keys_to_lower
|
10
|
-
from .exception import log_and_raise
|
11
12
|
|
12
13
|
|
13
14
|
class StringSet(Metadata):
|
14
15
|
"""Hosts a StringSet in the given format"""
|
15
16
|
|
17
|
+
class StringSetError(InputError):
|
18
|
+
"""An error that occurred while parsing a StringSet definition"""
|
19
|
+
|
16
20
|
KEY_VALUES: Final[str] = "Values".lower()
|
17
21
|
|
18
22
|
ValueType = Union[list[str], dict[str, dict]]
|
19
23
|
StringSetType = dict[str, Union[dict, ValueType]]
|
20
24
|
|
21
25
|
_ERR_KEY_MISSING = "Missing mandatory key '{}' in StringSet definition {}."
|
26
|
+
_ERR_VALUE_DEFINITION = "StringSet could not be parsed."
|
22
27
|
|
23
|
-
def __init__(self, definitions=None):
|
28
|
+
def __init__(self, definitions: dict[str, Any] | list[Any] | None = None):
|
24
29
|
super().__init__(definitions)
|
25
30
|
self._value_container: ValueContainer = ValueContainer(definitions)
|
26
31
|
|
27
32
|
@classmethod
|
28
33
|
def from_dict(cls, definition: StringSetType) -> StringSet:
|
29
|
-
"""
|
34
|
+
"""
|
35
|
+
Returns StringSet initialised from `definition`
|
36
|
+
|
37
|
+
Args:
|
38
|
+
definition: dictionary representation of string set
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
new StringSet
|
42
|
+
|
43
|
+
Raises:
|
44
|
+
StringSetError: if definitions are incomplete or erroneous, logged on level "ERROR"
|
45
|
+
"""
|
30
46
|
string_set = cls(definition)
|
31
47
|
definition = keys_to_lower(definition)
|
32
48
|
if cls.KEY_VALUES in definition:
|
33
|
-
|
49
|
+
try:
|
50
|
+
string_set._value_container = ValueContainer(definition[cls.KEY_VALUES])
|
51
|
+
except ValueContainer.ParseError as e:
|
52
|
+
raise log_error(StringSet.StringSetError(StringSet._ERR_VALUE_DEFINITION)) from e
|
34
53
|
else:
|
35
|
-
|
54
|
+
raise log_error(StringSet.StringSetError(cls._ERR_KEY_MISSING.format(cls.KEY_VALUES, definition)))
|
36
55
|
return string_set
|
37
56
|
|
38
57
|
def _to_dict(self) -> dict[str, dict[str, dict[str, dict[str, dict]]]]:
|
@@ -43,6 +62,6 @@ class StringSet(Metadata):
|
|
43
62
|
"""Returns values and their associated MetadataComponent"""
|
44
63
|
return self._value_container.values
|
45
64
|
|
46
|
-
def is_in_set(self, key:
|
65
|
+
def is_in_set(self, key: Any) -> bool:
|
47
66
|
"""Returns True if `key` is a valid name in this StringSet"""
|
48
67
|
return self._value_container.has_value(key)
|
fameio/input/schema/agenttype.py
CHANGED
@@ -32,7 +32,10 @@ class AgentType(Metadata):
|
|
32
32
|
Initialise a new AgentType
|
33
33
|
|
34
34
|
Args:
|
35
|
-
name: name of the AgenType
|
35
|
+
name: name of the AgenType
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
SchemaError: if name is None, empty, or only whitespaces, logged with level "ERROR"
|
36
39
|
"""
|
37
40
|
super().__init__()
|
38
41
|
if not name or name.isspace():
|
@@ -53,6 +56,9 @@ class AgentType(Metadata):
|
|
53
56
|
|
54
57
|
Returns:
|
55
58
|
a new instance of AgentType
|
59
|
+
|
60
|
+
Raises:
|
61
|
+
SchemaError: if definitions are invalid, logged with level "ERROR"
|
56
62
|
"""
|
57
63
|
agent_type = cls(name)
|
58
64
|
agent_type._extract_metadata(definitions)
|
@@ -83,7 +89,20 @@ class AgentType(Metadata):
|
|
83
89
|
|
84
90
|
@staticmethod
|
85
91
|
def _read_values(section: str, agent_type: str, values: Any) -> ValueContainer:
|
86
|
-
"""
|
92
|
+
"""
|
93
|
+
Returns ValueContainer for `section` of in specifications of `agent_type` extracted from given `values`
|
94
|
+
|
95
|
+
Args:
|
96
|
+
section: key of the section that contains the values
|
97
|
+
agent_type: name of the agent type that the values are associated with
|
98
|
+
values: list or dict with value definitions
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
container for all the extracted values
|
102
|
+
|
103
|
+
Raises:
|
104
|
+
SchemaError: if the values are ill-formatted, logged with level "ERROR"
|
105
|
+
"""
|
87
106
|
try:
|
88
107
|
data = ValueContainer(values)
|
89
108
|
except InputError as e:
|
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
|
@@ -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
|
|
@@ -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,22 @@ 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
|
-
"""
|
86
|
+
"""
|
87
|
+
Loads Attribute from given `definition`
|
88
|
+
|
89
|
+
Args:
|
90
|
+
name: of attribute type
|
91
|
+
definition: of attribute type
|
92
|
+
|
93
|
+
Raises:
|
94
|
+
SchemaError: if attribute type is not properly defined, logged with level "ERROR"
|
95
|
+
"""
|
86
96
|
super().__init__(definition)
|
87
97
|
self._assert_is_allowed_name(name)
|
88
98
|
self._full_name = name
|
89
99
|
|
90
100
|
if not definition:
|
91
|
-
raise SchemaError(AttributeSpecs._EMPTY_DEFINITION.format(name))
|
101
|
+
raise log_error(SchemaError(AttributeSpecs._EMPTY_DEFINITION.format(name)))
|
92
102
|
definition = keys_to_lower(definition)
|
93
103
|
|
94
104
|
self._is_mandatory = self._get_is_mandatory(definition, name)
|
@@ -96,7 +106,7 @@ class AttributeSpecs(Metadata):
|
|
96
106
|
self._attr_type = self._get_type(definition, name)
|
97
107
|
|
98
108
|
if self._attr_type == AttributeType.TIME_SERIES and self._is_list:
|
99
|
-
raise SchemaError(AttributeSpecs._SERIES_LIST_DISALLOWED.format(name))
|
109
|
+
raise log_error(SchemaError(AttributeSpecs._SERIES_LIST_DISALLOWED.format(name)))
|
100
110
|
|
101
111
|
self._allowed_values = self._get_allowed_values(definition)
|
102
112
|
self._default_value = self._get_default_value(definition)
|
@@ -105,14 +115,22 @@ class AttributeSpecs(Metadata):
|
|
105
115
|
|
106
116
|
@staticmethod
|
107
117
|
def _assert_is_allowed_name(full_name: str) -> None:
|
108
|
-
"""
|
118
|
+
"""
|
119
|
+
Raises SchemaError if provided name is not allowed for Attributes
|
120
|
+
|
121
|
+
Args:
|
122
|
+
full_name: to be checked if it can serve as name for an attribute
|
123
|
+
|
124
|
+
Raises:
|
125
|
+
SchemaError: if name is not allowed, logged with level "ERROR"
|
126
|
+
"""
|
109
127
|
if full_name is None:
|
110
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
128
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
111
129
|
short_name = full_name.split(AttributeSpecs._SEPARATOR)[-1]
|
112
130
|
if len(short_name) == 0 or short_name.isspace():
|
113
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
131
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
114
132
|
if short_name.lower() in AttributeSpecs._DISALLOWED_NAMES:
|
115
|
-
raise SchemaError(AttributeSpecs._NAME_DISALLOWED)
|
133
|
+
raise log_error(SchemaError(AttributeSpecs._NAME_DISALLOWED))
|
116
134
|
|
117
135
|
@staticmethod
|
118
136
|
def _get_is_mandatory(definition: dict, name: str) -> bool:
|
@@ -132,13 +150,25 @@ class AttributeSpecs(Metadata):
|
|
132
150
|
|
133
151
|
@staticmethod
|
134
152
|
def _get_type(definition: dict, name: str) -> AttributeType:
|
135
|
-
"""
|
153
|
+
"""
|
154
|
+
Returns `AttributeType` from given definition
|
155
|
+
|
156
|
+
Args:
|
157
|
+
definition: of the attribute
|
158
|
+
name: of the attribute
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
type of attribute
|
162
|
+
|
163
|
+
Raises:
|
164
|
+
SchemaError: if no proper type can be extracted, logged with level "ERROR"
|
165
|
+
"""
|
136
166
|
if AttributeSpecs.KEY_TYPE in definition:
|
137
167
|
type_name = definition[AttributeSpecs.KEY_TYPE]
|
138
168
|
try:
|
139
169
|
return AttributeType[type_name.upper()]
|
140
170
|
except KeyError as e:
|
141
|
-
raise SchemaError(AttributeSpecs._INVALID_TYPE.format(type_name)) from e
|
171
|
+
raise log_error(SchemaError(AttributeSpecs._INVALID_TYPE.format(type_name))) from e
|
142
172
|
raise log_error(SchemaError(AttributeSpecs._MISSING_TYPE.format(name)))
|
143
173
|
|
144
174
|
def _get_allowed_values(self, definition: dict) -> ValueContainer:
|
@@ -150,7 +180,7 @@ class AttributeSpecs(Metadata):
|
|
150
180
|
allowed_values = self._read_values(value_definition)
|
151
181
|
return allowed_values
|
152
182
|
|
153
|
-
def _read_values(self, definition:
|
183
|
+
def _read_values(self, definition: dict | list) -> ValueContainer:
|
154
184
|
"""
|
155
185
|
Returns accepted values mapped to their additional metadata specifications extracted from given `definition`
|
156
186
|
Accepts lists of accepted values or dictionaries with (optional) metadata assigned to each value
|
@@ -160,6 +190,9 @@ class AttributeSpecs(Metadata):
|
|
160
190
|
|
161
191
|
Returns:
|
162
192
|
Mapping of acceptable values to their associated Metadata
|
193
|
+
|
194
|
+
Raises:
|
195
|
+
SchemaError: if values could not be parsed, logged with level "ERROR"
|
163
196
|
"""
|
164
197
|
try:
|
165
198
|
value_container = ValueContainer(definition)
|
@@ -167,16 +200,28 @@ class AttributeSpecs(Metadata):
|
|
167
200
|
self._convert_to_data_type(value, self.KEY_VALUES)
|
168
201
|
return value_container
|
169
202
|
except ValueContainer.ParseError as e:
|
170
|
-
raise SchemaError(AttributeSpecs._VALUES_ILL_FORMAT.format(definition)) from e
|
203
|
+
raise log_error(SchemaError(AttributeSpecs._VALUES_ILL_FORMAT.format(definition))) from e
|
204
|
+
|
205
|
+
def _convert_to_data_type(self, value: str, section: str) -> int | float | str:
|
206
|
+
"""
|
207
|
+
Returns a given single `value` in `section` converted to this Attribute's data type
|
208
|
+
|
209
|
+
Args:
|
210
|
+
value: to be converted
|
211
|
+
section: that contains the value
|
171
212
|
|
172
|
-
|
173
|
-
|
213
|
+
Returns:
|
214
|
+
value with type matching this attribute type
|
215
|
+
|
216
|
+
Raises:
|
217
|
+
SchemaError: if value does not match this type of attribute, logged with level "ERROR"
|
218
|
+
"""
|
174
219
|
try:
|
175
220
|
return self._attr_type.convert_string_to_type(value)
|
176
221
|
except ValueError as e:
|
177
|
-
raise SchemaError(AttributeSpecs._INCOMPATIBLE.format(value, section, self._attr_type)) from e
|
222
|
+
raise log_error(SchemaError(AttributeSpecs._INCOMPATIBLE.format(value, section, self._attr_type))) from e
|
178
223
|
|
179
|
-
def _get_default_value(self, definition: dict) ->
|
224
|
+
def _get_default_value(self, definition: dict) -> int | float | str | list | None:
|
180
225
|
"""Returns default value(s) from given definitions, or None if no default is specified"""
|
181
226
|
if AttributeSpecs.KEY_DEFAULT in definition:
|
182
227
|
provided_value = definition[AttributeSpecs.KEY_DEFAULT]
|
@@ -186,15 +231,39 @@ class AttributeSpecs(Metadata):
|
|
186
231
|
return None
|
187
232
|
|
188
233
|
def _convert_list(self, values) -> list:
|
189
|
-
"""
|
234
|
+
"""
|
235
|
+
Converts all entries in given `values` list to this attribute data type and returns this new list
|
236
|
+
|
237
|
+
Args:
|
238
|
+
values: to be converted to a list of default values
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
default values
|
242
|
+
|
243
|
+
Raises:
|
244
|
+
SchemaError: if provided default is not a list, logged with level "ERROR"
|
245
|
+
"""
|
190
246
|
if isinstance(values, list):
|
191
247
|
return [self._convert_and_test(item) for item in values]
|
192
|
-
raise SchemaError(AttributeSpecs._DEFAULT_NOT_LIST.format(values))
|
248
|
+
raise log_error(SchemaError(AttributeSpecs._DEFAULT_NOT_LIST.format(values)))
|
193
249
|
|
194
250
|
def _convert_and_test(self, value: str):
|
195
|
-
"""
|
251
|
+
"""
|
252
|
+
Converts a given single `value` to this Attribute's data type and tests if the value is allowed
|
253
|
+
|
254
|
+
Args:
|
255
|
+
value: to be converted and tested
|
256
|
+
|
257
|
+
Returns:
|
258
|
+
the value converted to the data type matching this attribute type
|
259
|
+
|
260
|
+
Raises:
|
261
|
+
SchemaError: if the provided default could not be converted or is not allowed, logged with level "ERROR"
|
262
|
+
"""
|
263
|
+
if isinstance(value, (list, dict)):
|
264
|
+
raise log_error(SchemaError(self._DEFAULT_NOT_SIMPLE.format(value)))
|
196
265
|
if self.has_value_restrictions and (not self._allowed_values.has_value(value)):
|
197
|
-
raise SchemaError(AttributeSpecs._DEFAULT_DISALLOWED.format(value))
|
266
|
+
raise log_error(SchemaError(AttributeSpecs._DEFAULT_DISALLOWED.format(value)))
|
198
267
|
return self._convert_to_data_type(value, self.KEY_DEFAULT)
|
199
268
|
|
200
269
|
@staticmethod
|
@@ -248,7 +317,7 @@ class AttributeSpecs(Metadata):
|
|
248
317
|
return self._default_value is not None
|
249
318
|
|
250
319
|
@property
|
251
|
-
def default_value(self) ->
|
320
|
+
def default_value(self) -> Any | None:
|
252
321
|
"""Return the default value of this attribute, or None if no default is specified"""
|
253
322
|
return self._default_value
|
254
323
|
|
@@ -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,7 +6,7 @@ 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
|
|
@@ -21,7 +21,7 @@ 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] = []
|
@@ -36,6 +36,9 @@ class JavaPackages:
|
|
36
36
|
|
37
37
|
Returns:
|
38
38
|
new instance of JavaPackages
|
39
|
+
|
40
|
+
Raises:
|
41
|
+
SchemaError: if definitions are incomplete or erroneous, logged on level "ERROR"
|
39
42
|
"""
|
40
43
|
java_packages = cls()
|
41
44
|
definitions = keys_to_lower(definitions)
|
@@ -45,11 +48,11 @@ class JavaPackages:
|
|
45
48
|
java_packages._portables = definitions.get(JavaPackages.KEY_PORTABLE, [])
|
46
49
|
|
47
50
|
if not java_packages._agents:
|
48
|
-
|
51
|
+
raise log_error(SchemaError(JavaPackages._ERR_MISSING_AGENTS))
|
49
52
|
if not java_packages._data_items:
|
50
53
|
log().info(JavaPackages._INFO_MISSING_DATA_ITEMS)
|
51
54
|
if not java_packages._portables:
|
52
|
-
|
55
|
+
raise log_error(SchemaError(JavaPackages._ERR_MISSING_PORTABLES))
|
53
56
|
|
54
57
|
return java_packages
|
55
58
|
|