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.
Files changed (52) hide show
  1. fameio/cli/convert_results.py +6 -4
  2. fameio/cli/make_config.py +6 -4
  3. fameio/cli/parser.py +41 -29
  4. fameio/input/__init__.py +1 -1
  5. fameio/input/loader/__init__.py +9 -7
  6. fameio/input/loader/controller.py +59 -8
  7. fameio/input/loader/loader.py +14 -7
  8. fameio/input/metadata.py +35 -13
  9. fameio/input/resolver.py +5 -4
  10. fameio/input/scenario/agent.py +50 -16
  11. fameio/input/scenario/attribute.py +14 -15
  12. fameio/input/scenario/contract.py +152 -43
  13. fameio/input/scenario/exception.py +44 -18
  14. fameio/input/scenario/fameiofactory.py +63 -7
  15. fameio/input/scenario/generalproperties.py +17 -6
  16. fameio/input/scenario/scenario.py +111 -28
  17. fameio/input/scenario/stringset.py +27 -8
  18. fameio/input/schema/agenttype.py +21 -2
  19. fameio/input/schema/attribute.py +91 -22
  20. fameio/input/schema/java_packages.py +8 -5
  21. fameio/input/schema/schema.py +35 -9
  22. fameio/input/validator.py +22 -15
  23. fameio/input/writer.py +136 -36
  24. fameio/logs.py +3 -31
  25. fameio/output/__init__.py +5 -1
  26. fameio/output/agent_type.py +86 -23
  27. fameio/output/conversion.py +47 -29
  28. fameio/output/csv_writer.py +88 -18
  29. fameio/output/data_transformer.py +7 -14
  30. fameio/output/input_dao.py +62 -21
  31. fameio/output/output_dao.py +26 -4
  32. fameio/output/reader.py +58 -13
  33. fameio/output/yaml_writer.py +15 -6
  34. fameio/scripts/__init__.py +9 -2
  35. fameio/scripts/convert_results.py +123 -50
  36. fameio/scripts/convert_results.py.license +1 -1
  37. fameio/scripts/exception.py +7 -0
  38. fameio/scripts/make_config.py +34 -12
  39. fameio/scripts/make_config.py.license +1 -1
  40. fameio/series.py +117 -33
  41. fameio/time.py +74 -17
  42. fameio/tools.py +7 -5
  43. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/METADATA +19 -13
  44. fameio-3.2.0.dist-info/RECORD +56 -0
  45. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/WHEEL +1 -1
  46. CHANGELOG.md +0 -288
  47. fameio-3.1.1.dist-info/RECORD +0 -56
  48. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSE.txt +0 -0
  49. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
  50. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
  51. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
  52. {fameio-3.1.1.dist-info → fameio-3.2.0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
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 get_or_default, get_or_raise
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
- """Parse general properties from provided `definitions`"""
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 = get_or_default(definitions, GeneralProperties.KEY_RUN, 1)
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 = get_or_default(simulation_definition, GeneralProperties.KEY_SEED, 1)
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: 2024 German Aerospace Center <fame@dlr.de>
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
- """Parse scenario from provided `definitions` using given `factory`"""
41
- definitions = keys_to_lower(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
- schema = factory.new_schema_from_dict(get_or_raise(definitions, Scenario.KEY_SCHEMA, Scenario._MISSING_KEY))
44
- general_props = factory.new_general_properties_from_dict(
45
- get_or_raise(definitions, Scenario.KEY_GENERAL, Scenario._MISSING_KEY)
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
- for name, string_set_definition in get_or_default(definitions, Scenario.KEY_STRING_SETS, {}).items():
51
- scenario.add_string_set(name, factory.new_string_set_from_dict(string_set_definition))
52
+ Returns:
53
+ new Scenario
52
54
 
53
- for agent_definition in get_or_default(definitions, Scenario.KEY_AGENTS, []):
54
- scenario.add_agent(factory.new_agent_from_dict(agent_definition))
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
- for multi_contract_definition in get_or_default(definitions, Scenario.KEY_CONTRACTS, []):
57
- for single_contract_definition in Contract.split_contract_definitions(multi_contract_definition):
58
- scenario.add_contract(factory.new_contract_from_dict(single_contract_definition))
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: 2024 German Aerospace Center <fame@dlr.de>
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 Union, Final
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
- """Returns StringSet initialised from `definition`"""
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
- string_set._value_container = ValueContainer(definition[cls.KEY_VALUES])
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
- log_and_raise(cls._ERR_KEY_MISSING.format(cls.KEY_VALUES, definition))
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: str) -> bool:
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)
@@ -32,7 +32,10 @@ class AgentType(Metadata):
32
32
  Initialise a new AgentType
33
33
 
34
34
  Args:
35
- name: name of the AgenType - must not be None, empty or only whitespaces
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
- """Returns ValueContainer for `section` of in specifications of `agent_type` extracted from given `values`"""
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:
@@ -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, Optional, Final, Union
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) -> Union[int, float, 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
- """Loads Attribute from given `definition`"""
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
- """Raises SchemaError if provided name is not allowed for Attributes"""
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
- """Returns `AttributeType` from given definition; Raises an exception if no proper type can be extracted"""
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: [dict, list]) -> ValueContainer:
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
- def _convert_to_data_type(self, value: str, section: str) -> Union[int, float, str]:
173
- """Returns a given single `value` in `section` converted to this Attribute's data type"""
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) -> Optional[Union[int, float, str, list]]:
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
- """Converts all entries in given `values` list to this attribute data type and returns this new list"""
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
- """Converts a given single `value` to this Attribute's data type and tests if the value is allowed"""
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) -> Optional[Any]:
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: 2024 German Aerospace Center <fame@dlr.de>
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 log_error_and_raise, log
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
- log_error_and_raise(SchemaError(JavaPackages._ERR_MISSING_AGENTS))
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
- log_error_and_raise(SchemaError(JavaPackages._ERR_MISSING_PORTABLES))
55
+ raise log_error(SchemaError(JavaPackages._ERR_MISSING_PORTABLES))
53
56
 
54
57
  return java_packages
55
58