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.
Files changed (57) hide show
  1. fameio/cli/convert_results.py +10 -10
  2. fameio/cli/make_config.py +9 -9
  3. fameio/cli/options.py +6 -4
  4. fameio/cli/parser.py +87 -51
  5. fameio/cli/reformat.py +58 -0
  6. fameio/input/__init__.py +4 -4
  7. fameio/input/loader/__init__.py +13 -13
  8. fameio/input/loader/controller.py +64 -18
  9. fameio/input/loader/loader.py +25 -16
  10. fameio/input/metadata.py +57 -38
  11. fameio/input/resolver.py +9 -10
  12. fameio/input/scenario/agent.py +62 -26
  13. fameio/input/scenario/attribute.py +93 -40
  14. fameio/input/scenario/contract.py +160 -56
  15. fameio/input/scenario/exception.py +41 -18
  16. fameio/input/scenario/fameiofactory.py +57 -6
  17. fameio/input/scenario/generalproperties.py +22 -12
  18. fameio/input/scenario/scenario.py +117 -38
  19. fameio/input/scenario/stringset.py +29 -11
  20. fameio/input/schema/agenttype.py +27 -10
  21. fameio/input/schema/attribute.py +108 -45
  22. fameio/input/schema/java_packages.py +14 -12
  23. fameio/input/schema/schema.py +39 -15
  24. fameio/input/validator.py +198 -54
  25. fameio/input/writer.py +137 -46
  26. fameio/logs.py +28 -47
  27. fameio/output/__init__.py +5 -1
  28. fameio/output/agent_type.py +89 -28
  29. fameio/output/conversion.py +52 -37
  30. fameio/output/csv_writer.py +107 -27
  31. fameio/output/data_transformer.py +17 -24
  32. fameio/output/execution_dao.py +170 -0
  33. fameio/output/input_dao.py +71 -33
  34. fameio/output/output_dao.py +33 -11
  35. fameio/output/reader.py +64 -21
  36. fameio/output/yaml_writer.py +16 -8
  37. fameio/scripts/__init__.py +22 -4
  38. fameio/scripts/convert_results.py +126 -52
  39. fameio/scripts/convert_results.py.license +1 -1
  40. fameio/scripts/exception.py +7 -0
  41. fameio/scripts/make_config.py +34 -13
  42. fameio/scripts/make_config.py.license +1 -1
  43. fameio/scripts/reformat.py +71 -0
  44. fameio/scripts/reformat.py.license +3 -0
  45. fameio/series.py +174 -59
  46. fameio/time.py +79 -25
  47. fameio/tools.py +48 -8
  48. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/METADATA +50 -34
  49. fameio-3.3.0.dist-info/RECORD +60 -0
  50. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/WHEEL +1 -1
  51. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/entry_points.txt +1 -0
  52. CHANGELOG.md +0 -288
  53. fameio-3.1.1.dist-info/RECORD +0 -56
  54. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSE.txt +0 -0
  55. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
  56. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
  57. {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
@@ -1,35 +1,58 @@
1
1
  # SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- from typing import NoReturn, Any, Union
4
+ from __future__ import annotations
5
+
6
+ from typing import Any
5
7
 
6
8
  from fameio.input import ScenarioError
7
- from fameio.logs import log_error, log
9
+ from fameio.logs import log_error
8
10
 
9
11
  _DEFAULT_USED = "Using default value '{}' for missing key '{}'"
10
12
 
11
13
 
12
- def log_and_raise(message: str) -> NoReturn:
13
- """Raises ScenarioError with given `message`"""
14
- raise log_error(ScenarioError(message))
14
+ def log_scenario_error(message: str) -> ScenarioError:
15
+ """Creates exception with given `message`, logs it on level "Error" and returns it.
16
+
17
+ Args:
18
+ message: to be logged and included in the exception if key is missing
19
+
20
+ Returns:
21
+ created ScenarioError, logged on level "ERROR"
22
+ """
23
+ error = ScenarioError(message)
24
+ log_error(error)
25
+ return error
26
+
15
27
 
28
+ def get_or_raise(dictionary: dict, key: str, error_message: str) -> Any:
29
+ """Returns value associated with `key` in given `dictionary`, or raises exception if key or value is missing.
16
30
 
17
- def get_or_raise(dictionary: dict, key: str, message: str) -> Union[Any, NoReturn]:
18
- """Returns value associated with `key` in given `dictionary`, or raises ScenarioException if key is missing"""
31
+ Args:
32
+ dictionary: to search the key in
33
+ key: to be searched
34
+ error_message: to be logged and included in the raised exception if key is missing
35
+
36
+ Returns:
37
+ value associated with given key in given dictionary
38
+
39
+ Raises:
40
+ ScenarioError: if given key is not in given dictionary or value is None, logged on level "ERROR"
41
+ """
19
42
  if key not in dictionary or dictionary[key] is None:
20
- raise log_error(ScenarioError(message.format(key)))
43
+ raise log_scenario_error(error_message.format(key))
21
44
  return dictionary[key]
22
45
 
23
46
 
24
- def assert_or_raise(assertion: bool, msg: str) -> None:
25
- """Raises new ScenarioError with given `msg` if `assertion` is False"""
26
- if not assertion:
27
- raise log_error(ScenarioError(msg))
47
+ def assert_or_raise(assertion: bool, error_message: str) -> None:
48
+ """Raises exception with given `error_message` if `assertion` is False.
28
49
 
50
+ Args:
51
+ assertion: expression that must be True, else an exception is raised
52
+ error_message: to be logged and included in the raised exception if key is missing
29
53
 
30
- def get_or_default(dictionary: dict, key: str, default_value) -> Any:
31
- """Returns value associated with `key` in given `dictionary`, or the given `default_value` if key is missing"""
32
- if key in dictionary and dictionary[key] is not None:
33
- return dictionary[key]
34
- log().debug(_DEFAULT_USED.format(default_value, key))
35
- return default_value
54
+ Raises:
55
+ ScenarioError: if assertion is False, logged on level "ERROR"
56
+ """
57
+ if not assertion:
58
+ raise log_scenario_error(error_message)
@@ -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 fameio.input.schema import Schema
@@ -10,30 +10,81 @@ from .stringset import StringSet
10
10
 
11
11
  class FameIOFactory:
12
12
  """Factory used to instantiate the types defined in a scenario file.
13
+
13
14
  This allows a client to subclass some types in order to extend what a scenario can contain.
14
15
  """
15
16
 
16
17
  @staticmethod
17
18
  def new_schema_from_dict(definitions: dict) -> Schema:
18
- """Constructs a new Schema from provided `definitions`"""
19
+ """Loads given dictionary `definitions` into a new schema.
20
+
21
+ Args:
22
+ definitions: dictionary representation of schema
23
+
24
+ Returns:
25
+ new Schema
26
+
27
+ Raises:
28
+ SchemaError: if definitions are incomplete or erroneous, logged on level "ERROR"
29
+ """
19
30
  return Schema.from_dict(definitions)
20
31
 
21
32
  @staticmethod
22
33
  def new_general_properties_from_dict(definitions: dict) -> GeneralProperties:
23
- """Constructs a new GeneralProperties instance from provided `definitions`"""
34
+ """Parses general properties from provided `definitions`.
35
+
36
+ Args:
37
+ definitions: dictionary representation of general properties
38
+
39
+ Returns:
40
+ new GeneralProperties
41
+
42
+ Raises:
43
+ ScenarioError: if definitions are incomplete or erroneous, logged on level "ERROR"
44
+ """
24
45
  return GeneralProperties.from_dict(definitions)
25
46
 
26
47
  @staticmethod
27
48
  def new_agent_from_dict(definitions: dict) -> Agent:
28
- """Constructs a new Agent from provided `definitions`"""
49
+ """Parses an agent from provided `definitions`.
50
+
51
+ Args:
52
+ definitions: dictionary representation of an agent
53
+
54
+ Returns:
55
+ new agent
56
+
57
+ Raises:
58
+ ScenarioError: if definitions are incomplete or erroneous, logged on level "ERROR"
59
+ """
29
60
  return Agent.from_dict(definitions)
30
61
 
31
62
  @staticmethod
32
63
  def new_contract_from_dict(definitions: dict) -> Contract:
33
- """Constructs a new Contract from provided `definitions`"""
64
+ """Parses contract from given `definitions`.
65
+
66
+ Args:
67
+ definitions: dictionary representation of a contract
68
+
69
+ Returns:
70
+ new contract
71
+
72
+ Raises:
73
+ ContractError: if definitions are incomplete or erroneous, logged on level "ERROR"
74
+ """
34
75
  return Contract.from_dict(definitions)
35
76
 
36
77
  @staticmethod
37
78
  def new_string_set_from_dict(definition: StringSet.StringSetType) -> StringSet:
38
- """Constructs a new StringSet from provided `definitions`"""
79
+ """Returns string set initialised from `definition`.
80
+
81
+ Args:
82
+ definition: dictionary representation of string set
83
+
84
+ Returns:
85
+ new string set
86
+
87
+ Raises:
88
+ StringSetError: if definitions are incomplete or erroneous, logged on level "ERROR"
89
+ """
39
90
  return StringSet.from_dict(definition)
@@ -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,11 +8,11 @@ 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:
15
- """Hosts general properties of a scenario"""
15
+ """Hosts general properties of a scenario."""
16
16
 
17
17
  KEY_RUN: Final[str] = "RunId".lower()
18
18
  KEY_SIMULATION = "Simulation".lower()
@@ -39,9 +39,19 @@ class GeneralProperties:
39
39
 
40
40
  @classmethod
41
41
  def from_dict(cls, definitions: dict) -> GeneralProperties:
42
- """Parse general properties from provided `definitions`"""
42
+ """Parses general properties from provided `definitions`.
43
+
44
+ Args:
45
+ definitions: dictionary representation of general properties
46
+
47
+ Returns:
48
+ new GeneralProperties
49
+
50
+ Raises:
51
+ ScenarioError: if definitions are incomplete or erroneous, logged on level "ERROR"
52
+ """
43
53
  definitions = keys_to_lower(definitions)
44
- run_id = get_or_default(definitions, GeneralProperties.KEY_RUN, 1)
54
+ run_id = definitions.get(GeneralProperties.KEY_RUN, 1)
45
55
 
46
56
  simulation_definition = keys_to_lower(
47
57
  get_or_raise(
@@ -64,12 +74,12 @@ class GeneralProperties:
64
74
  GeneralProperties._ERR_MISSING_KEY,
65
75
  )
66
76
  )
67
- random_seed = get_or_default(simulation_definition, GeneralProperties.KEY_SEED, 1)
77
+ random_seed = simulation_definition.get(GeneralProperties.KEY_SEED, 1)
68
78
  return cls(run_id, start_time, stop_time, random_seed)
69
79
 
70
80
  def to_dict(self) -> dict:
71
- """Serializes the general properties to a dict"""
72
- result = {self.KEY_RUN: self._run_id}
81
+ """Serializes the general properties to a dict."""
82
+ result: dict = {self.KEY_RUN: self._run_id}
73
83
  simulation_dict = {
74
84
  self.KEY_START: self.simulation_start_time,
75
85
  self.KEY_STOP: self.simulation_stop_time,
@@ -80,20 +90,20 @@ class GeneralProperties:
80
90
 
81
91
  @property
82
92
  def run_id(self) -> int:
83
- """Returns the run ID"""
93
+ """Returns the run ID."""
84
94
  return self._run_id
85
95
 
86
96
  @property
87
97
  def simulation_start_time(self) -> int:
88
- """Returns the simulation start time"""
98
+ """Returns the simulation start time."""
89
99
  return self._simulation_start_time
90
100
 
91
101
  @property
92
102
  def simulation_stop_time(self) -> int:
93
- """Returns the simulation stop time"""
103
+ """Returns the simulation stop time."""
94
104
  return self._simulation_stop_time
95
105
 
96
106
  @property
97
107
  def simulation_random_seed(self) -> int:
98
- """Returns the simulation random seed"""
108
+ """Returns the simulation random seed."""
99
109
  return self._simulation_random_seed
@@ -1,23 +1,24 @@
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):
20
- """Definition of a scenario"""
21
+ """Definition of a scenario."""
21
22
 
22
23
  KEY_SCHEMA: Final[str] = "Schema".lower()
23
24
  KEY_GENERAL: Final[str] = "GeneralProperties".lower()
@@ -26,42 +27,120 @@ 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
+ """Parses scenario from provided `definitions` using given `factory`.
42
46
 
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)
47
+ Args:
48
+ definitions: dictionary representation of scenario
49
+ factory: helper class with static helpers to instantiate scenario components
49
50
 
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))
51
+ Returns:
52
+ new Scenario
52
53
 
53
- for agent_definition in get_or_default(definitions, Scenario.KEY_AGENTS, []):
54
- scenario.add_agent(factory.new_agent_from_dict(agent_definition))
54
+ Raises:
55
+ ScenarioError: if scenario definitions are incomplete or erroneous, logged with level "ERROR"
56
+ """
57
+ definitions = keys_to_lower(definitions)
55
58
 
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))
59
+ schema = Scenario._extract_schema(definitions, factory)
60
+ general_properties_definition = get_or_raise(definitions, Scenario.KEY_GENERAL, Scenario._MISSING_KEY)
61
+ general_properties = factory.new_general_properties_from_dict(general_properties_definition)
62
+ scenario = cls(schema, general_properties)
63
+ scenario._extract_metadata(definitions)
59
64
 
65
+ scenario._string_sets = Scenario._extract_string_sets(definitions, factory)
66
+ scenario._agents = [
67
+ factory.new_agent_from_dict(agent_definition)
68
+ for agent_definition in definitions.get(Scenario.KEY_AGENTS, [])
69
+ ]
70
+ scenario._contracts = Scenario._extract_contracts(definitions, factory)
60
71
  return scenario
61
72
 
73
+ @staticmethod
74
+ def _extract_schema(definitions: dict, factory: FameIOFactory) -> Schema:
75
+ """Extracts schema from given definitions and creates Schema from it.
76
+
77
+ Args:
78
+ definitions: dictionary representation of scenario
79
+ factory: helper class with static helpers to instantiate scenario components
80
+
81
+ Returns:
82
+ new schema
83
+
84
+ Raises:
85
+ ScenarioError: if schema definitions are missing, incomplete, or erroneous; logged on level "ERROR"
86
+ """
87
+ schema_definition = get_or_raise(definitions, Scenario.KEY_SCHEMA, Scenario._MISSING_KEY)
88
+ try:
89
+ return factory.new_schema_from_dict(schema_definition)
90
+ except SchemaError as e:
91
+ raise log_scenario_error(Scenario._ERR_SCHEMA) from e
92
+
93
+ @staticmethod
94
+ def _extract_string_sets(definitions: dict, factory: FameIOFactory) -> dict[str, StringSet]:
95
+ """Extracts string sets from given definitions and creates dictionary from it.
96
+
97
+ Args:
98
+ definitions: dictionary representation of scenario
99
+ factory: helper class with static helpers to instantiate scenario components
100
+
101
+ Returns:
102
+ dictionary of string set names associated with their corresponding string set
103
+
104
+ Raises:
105
+ ScenarioError: if string set definitions are incomplete or erroneous; logged on level "ERROR"
106
+ """
107
+ string_sets = {}
108
+ for name, string_set_definition in definitions.get(Scenario.KEY_STRING_SETS, {}).items():
109
+ try:
110
+ string_sets[name] = factory.new_string_set_from_dict(string_set_definition)
111
+ except StringSet.StringSetError as e:
112
+ raise log_scenario_error(Scenario._ERR_STRING_SET.format(name)) from e
113
+ return string_sets
114
+
115
+ @staticmethod
116
+ def _extract_contracts(definitions: dict, factory: FameIOFactory) -> list[Contract]:
117
+ """Extracts contracts from given definitions.
118
+
119
+ Args:
120
+ definitions: dictionary representation of scenario
121
+ factory: helper class with static helpers to instantiate scenario components
122
+
123
+ Returns:
124
+ list of all created one-to-one contracts
125
+
126
+ Raises:
127
+ ScenarioError: if contract definitions are incomplete or erroneous; logged on level "ERROR"
128
+ """
129
+ contracts = []
130
+ for multi_contract_definition in definitions.get(Scenario.KEY_CONTRACTS, []):
131
+ try:
132
+ for single_contract_definition in Contract.split_contract_definitions(multi_contract_definition):
133
+ try:
134
+ contracts.append(factory.new_contract_from_dict(single_contract_definition))
135
+ except Contract.ContractError as e:
136
+ raise log_scenario_error(Scenario._ERR_CONTRACT.format(single_contract_definition)) from e
137
+ except Contract.ContractError as e:
138
+ raise log_scenario_error(Scenario._ERR_MULTI_CONTRACT.format(multi_contract_definition)) from e
139
+ return contracts
140
+
62
141
  def _to_dict(self) -> dict:
63
- """Serializes the scenario content to a dict"""
64
- result = {
142
+ """Serializes the scenario content to a dict."""
143
+ result: dict[str, Any] = {
65
144
  Scenario.KEY_GENERAL: self.general_properties.to_dict(),
66
145
  Scenario.KEY_SCHEMA: self.schema.to_dict(),
67
146
  }
@@ -81,37 +160,37 @@ class Scenario(Metadata):
81
160
 
82
161
  @property
83
162
  def agents(self) -> list[Agent]:
84
- """Returns all the agents of this scenario as a list"""
163
+ """Returns all the agents of this scenario as a list."""
85
164
  return self._agents
86
165
 
87
166
  def add_agent(self, agent: Agent) -> None:
88
- """Adds a new agent to this scenario"""
167
+ """Adds a new agent to this scenario."""
89
168
  self._agents.append(agent)
90
169
 
91
170
  @property
92
171
  def contracts(self) -> list[Contract]:
93
- """Returns all the contracts of this scenario as a list"""
172
+ """Returns all the contracts of this scenario as a list."""
94
173
  return self._contracts
95
174
 
96
175
  def add_contract(self, contract: Contract) -> None:
97
- """Adds a new contract to this scenario"""
176
+ """Adds a new contract to this scenario."""
98
177
  self._contracts.append(contract)
99
178
 
100
179
  @property
101
180
  def schema(self) -> Schema:
102
- """Returns Schema associated with this scenario"""
181
+ """Returns Schema associated with this scenario."""
103
182
  return self._schema
104
183
 
105
184
  @property
106
185
  def general_properties(self) -> GeneralProperties:
107
- """Returns General properties of this scenario"""
186
+ """Returns General properties of this scenario."""
108
187
  return self._general_props
109
188
 
110
189
  @property
111
190
  def string_sets(self) -> dict[str, StringSet]:
112
- """Returns StringSets of this scenario"""
191
+ """Returns StringSets of this scenario."""
113
192
  return self._string_sets
114
193
 
115
194
  def add_string_set(self, name: str, string_set: StringSet) -> None:
116
- """Adds `string_set` with `name`"""
195
+ """Adds `string_set` with `name`."""
117
196
  self._string_sets[name] = string_set
@@ -1,17 +1,21 @@
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
- """Hosts a StringSet in the given format"""
15
+ """Hosts a StringSet in the given format."""
16
+
17
+ class StringSetError(InputError):
18
+ """An error that occurred while parsing a StringSet definition."""
15
19
 
16
20
  KEY_VALUES: Final[str] = "Values".lower()
17
21
 
@@ -19,20 +23,34 @@ class StringSet(Metadata):
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
+ """Returns StringSet initialised from `definition`.
35
+
36
+ Args:
37
+ definition: dictionary representation of string set
38
+
39
+ Returns:
40
+ new StringSet
41
+
42
+ Raises:
43
+ StringSetError: if definitions are incomplete or erroneous, logged on level "ERROR"
44
+ """
30
45
  string_set = cls(definition)
31
46
  definition = keys_to_lower(definition)
32
47
  if cls.KEY_VALUES in definition:
33
- string_set._value_container = ValueContainer(definition[cls.KEY_VALUES])
48
+ try:
49
+ string_set._value_container = ValueContainer(definition[cls.KEY_VALUES])
50
+ except ValueContainer.ParseError as e:
51
+ raise log_error(StringSet.StringSetError(StringSet._ERR_VALUE_DEFINITION)) from e
34
52
  else:
35
- log_and_raise(cls._ERR_KEY_MISSING.format(cls.KEY_VALUES, definition))
53
+ raise log_error(StringSet.StringSetError(cls._ERR_KEY_MISSING.format(cls.KEY_VALUES, definition)))
36
54
  return string_set
37
55
 
38
56
  def _to_dict(self) -> dict[str, dict[str, dict[str, dict[str, dict]]]]:
@@ -40,9 +58,9 @@ class StringSet(Metadata):
40
58
 
41
59
  @property
42
60
  def values(self) -> dict[str, MetadataComponent]:
43
- """Returns values and their associated MetadataComponent"""
61
+ """Returns values and their associated MetadataComponent."""
44
62
  return self._value_container.values
45
63
 
46
- def is_in_set(self, key: str) -> bool:
47
- """Returns True if `key` is a valid name in this StringSet"""
64
+ def is_in_set(self, key: Any) -> bool:
65
+ """Returns True if `key` is a valid name in this StringSet."""
48
66
  return self._value_container.has_value(key)
@@ -13,7 +13,7 @@ from .attribute import AttributeSpecs
13
13
 
14
14
 
15
15
  class AgentType(Metadata):
16
- """Schema definitions for an Agent type"""
16
+ """Schema definitions for an Agent type."""
17
17
 
18
18
  KEY_ATTRIBUTES: Final[str] = "Attributes".lower()
19
19
  KEY_PRODUCTS: Final[str] = "Products".lower()
@@ -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():
@@ -44,8 +47,7 @@ class AgentType(Metadata):
44
47
 
45
48
  @classmethod
46
49
  def from_dict(cls, name: str, definitions: dict) -> AgentType:
47
- """
48
- Creates AgentType with given `name` from specified dictionary
50
+ """Creates AgentType with given `name` from specified dictionary.
49
51
 
50
52
  Args:
51
53
  name: of the agent type
@@ -53,6 +55,9 @@ class AgentType(Metadata):
53
55
 
54
56
  Returns:
55
57
  a new instance of AgentType
58
+
59
+ Raises:
60
+ SchemaError: if definitions are invalid, logged with level "ERROR"
56
61
  """
57
62
  agent_type = cls(name)
58
63
  agent_type._extract_metadata(definitions)
@@ -83,7 +88,19 @@ class AgentType(Metadata):
83
88
 
84
89
  @staticmethod
85
90
  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`"""
91
+ """Returns ValueContainer for `section` in specifications of `agent_type` extracted from given `values`.
92
+
93
+ Args:
94
+ section: key of the section that contains the values
95
+ agent_type: name of the agent type that the values are associated with
96
+ values: list or dict with value definitions
97
+
98
+ Returns:
99
+ container for all the extracted values
100
+
101
+ Raises:
102
+ SchemaError: if the values are ill-formatted, logged with level "ERROR"
103
+ """
87
104
  try:
88
105
  data = ValueContainer(values)
89
106
  except InputError as e:
@@ -94,26 +111,26 @@ class AgentType(Metadata):
94
111
 
95
112
  @property
96
113
  def name(self) -> str:
97
- """Returns the agent type name"""
114
+ """Returns the agent type name."""
98
115
  return self._name
99
116
 
100
117
  @property
101
118
  def products(self) -> dict[str, MetadataComponent]:
102
- """Returns dict of products or an empty dict if no products are defined"""
119
+ """Returns dict of products or an empty dict if no products are defined."""
103
120
  return self._products.values
104
121
 
105
122
  def get_product_names(self) -> list[str]:
106
- """Returns list of product names or an empty list if no products are defined"""
123
+ """Returns list of product names or an empty list if no products are defined."""
107
124
  return self._products.as_list()
108
125
 
109
126
  @property
110
127
  def attributes(self) -> dict[str, AttributeSpecs]:
111
- """Returns list of Attributes of this agent or an empty list if no attributes are defined"""
128
+ """Returns list of Attributes of this agent or an empty list if no attributes are defined."""
112
129
  return self._attributes
113
130
 
114
131
  @property
115
132
  def outputs(self) -> dict[str, MetadataComponent]:
116
- """Returns list of outputs or an empty list if no outputs are defined"""
133
+ """Returns list of outputs or an empty list if no outputs are defined."""
117
134
  return self._outputs.values
118
135
 
119
136
  def _to_dict(self) -> dict: