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,8 +1,10 @@
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
+ from __future__ import annotations
5
+
4
6
  import argparse
5
- from typing import Any, Optional
7
+ from typing import Any
6
8
 
7
9
  from fameio.cli.options import Options, ResolveOptions, TimeOptions
8
10
  from fameio.cli.parser import (
@@ -24,8 +26,8 @@ CLI_DEFAULTS = {
24
26
  Options.FILE: None,
25
27
  Options.LOG_LEVEL: "WARN",
26
28
  Options.LOG_FILE: None,
27
- Options.AGENT_LIST: None,
28
29
  Options.OUTPUT: None,
30
+ Options.AGENT_LIST: None,
29
31
  Options.SINGLE_AGENT_EXPORT: False,
30
32
  Options.MEMORY_SAVING: False,
31
33
  Options.RESOLVE_COMPLEX_FIELD: ResolveOptions.SPLIT,
@@ -38,9 +40,8 @@ _INFILE_PATH_HELP = "Provide path to protobuf file"
38
40
  _OUTFILE_PATH_HELP = "Provide path to folder to store output .csv files"
39
41
 
40
42
 
41
- def handle_args(args: list[str], defaults: Optional[dict[Options, Any]] = None) -> dict[Options, Any]:
42
- """
43
- Handles command line arguments and returns `run_config` for convert_results script
43
+ def handle_args(args: list[str], defaults: dict[Options, Any] | None = None) -> dict[Options, Any]:
44
+ """Handles command line arguments and returns `run_config` for convert_results script.
44
45
 
45
46
  Args:
46
47
  args: list of (command line) arguments, e.g., ['-f', 'my_file']; arg values take precedence over defaults
@@ -54,9 +55,8 @@ def handle_args(args: list[str], defaults: Optional[dict[Options, Any]] = None)
54
55
  return map_namespace_to_options_dict(parsed)
55
56
 
56
57
 
57
- def _prepare_parser(defaults: Optional[dict[Options, Any]]) -> argparse.ArgumentParser:
58
- """
59
- Creates a parser with given defaults to handle `make_config` configuration arguments
58
+ def _prepare_parser(defaults: dict[Options, Any] | None) -> argparse.ArgumentParser:
59
+ """Creates a parser with given defaults to handle `make_config` configuration arguments.
60
60
 
61
61
  Returns:
62
62
  new parser using given defaults for its arguments; if a default is not specified, hard-coded defaults are used
@@ -80,5 +80,5 @@ def _prepare_parser(defaults: Optional[dict[Options, Any]]) -> argparse.Argument
80
80
 
81
81
 
82
82
  def _get_default(defaults: dict, option: Options) -> Any:
83
- """Returns default for given `option` or its cli default"""
83
+ """Returns default for given `option` or its cli default."""
84
84
  return defaults.get(option, CLI_DEFAULTS[option])
fameio/cli/make_config.py CHANGED
@@ -1,9 +1,11 @@
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
+ from __future__ import annotations
5
+
4
6
  import argparse
5
7
  from pathlib import Path
6
- from typing import Any, Optional
8
+ from typing import Any
7
9
 
8
10
  from fameio.cli.options import Options
9
11
  from fameio.cli.parser import (
@@ -31,9 +33,8 @@ _ENCODING_HELP = (
31
33
  )
32
34
 
33
35
 
34
- def handle_args(args: list[str], defaults: Optional[dict[Options, Any]] = None) -> dict[Options, Any]:
35
- """
36
- Converts given `arguments` and returns a configuration for the make_config script
36
+ def handle_args(args: list[str], defaults: dict[Options, Any] | None = None) -> dict[Options, Any]:
37
+ """Converts given `args` and returns a configuration for the make_config script.
37
38
 
38
39
  Args:
39
40
  args: list of (command line) arguments, e.g., ['-f', 'my_file']; arg values take precedence over defaults
@@ -47,9 +48,8 @@ def handle_args(args: list[str], defaults: Optional[dict[Options, Any]] = None)
47
48
  return map_namespace_to_options_dict(parsed)
48
49
 
49
50
 
50
- def _prepare_parser(defaults: Optional[dict[Options, Any]]) -> argparse.ArgumentParser:
51
- """
52
- Creates a parser with given defaults to handle `make_config` configuration arguments
51
+ def _prepare_parser(defaults: dict[Options, Any] | None) -> argparse.ArgumentParser:
52
+ """Creates a parser with given defaults to handle `make_config` configuration arguments.
53
53
 
54
54
  Returns:
55
55
  new parser using given defaults for its arguments; if a default is not specified, hard-coded defaults are used
@@ -65,5 +65,5 @@ def _prepare_parser(defaults: Optional[dict[Options, Any]]) -> argparse.Argument
65
65
 
66
66
 
67
67
  def _get_default(defaults: dict, option: Options) -> Any:
68
- """Returns default for given `option` or, if missing, its cli default"""
68
+ """Returns default for given `option` or, if missing, its cli default."""
69
69
  return defaults.get(option, CLI_DEFAULTS[option])
fameio/cli/options.py CHANGED
@@ -6,7 +6,7 @@ from enum import Enum, auto
6
6
 
7
7
 
8
8
  class ParsableEnum(Enum):
9
- """Extend this to create an enum that can be parsed with argparse"""
9
+ """Extend this to create an enum that can be parsed with argparse."""
10
10
 
11
11
  @classmethod
12
12
  def instantiate(cls, name: str) -> Enum:
@@ -20,7 +20,7 @@ class ParsableEnum(Enum):
20
20
 
21
21
 
22
22
  class Options(Enum):
23
- """Specifies command line configuration options"""
23
+ """Specifies command line configuration options."""
24
24
 
25
25
  FILE = auto()
26
26
  LOG_LEVEL = auto()
@@ -34,10 +34,12 @@ class Options(Enum):
34
34
  TIME_MERGING = auto()
35
35
  INPUT_RECOVERY = auto()
36
36
  INPUT_ENCODING = auto()
37
+ FILE_PATTERN = auto()
38
+ REPLACE = auto()
37
39
 
38
40
 
39
41
  class TimeOptions(ParsableEnum, Enum):
40
- """Specifies options for conversion of time in output"""
42
+ """Specifies options for conversion of time in output."""
41
43
 
42
44
  INT = auto()
43
45
  UTC = auto()
@@ -45,7 +47,7 @@ class TimeOptions(ParsableEnum, Enum):
45
47
 
46
48
 
47
49
  class ResolveOptions(ParsableEnum, Enum):
48
- """Specifies options for resolving complex fields in output files"""
50
+ """Specifies options for resolving complex fields in output files."""
49
51
 
50
52
  IGNORE = auto()
51
53
  SPLIT = auto()
fameio/cli/parser.py CHANGED
@@ -1,18 +1,19 @@
1
1
  # SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
+ from __future__ import annotations
5
+
4
6
  import copy
5
7
  from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction, Namespace
6
- from enum import Enum
7
8
  from pathlib import Path
8
- from typing import Optional, Any, Union
9
+ from typing import Any
9
10
 
10
11
  from fameio.cli.options import TimeOptions, ResolveOptions, Options
11
12
  from fameio.logs import LogLevel
12
13
 
13
14
  _ERR_INVALID_MERGING_DEFAULT = "Invalid merge-times default: needs list of 3 integers separated by spaces but was: '{}'"
14
15
 
15
- _OPTION_ARGUMENT_NAME: dict[str, Union[Options, dict]] = {
16
+ _OPTION_ARGUMENT_NAME: dict[str, Options] = {
16
17
  "file": Options.FILE,
17
18
  "log": Options.LOG_LEVEL,
18
19
  "logfile": Options.LOG_FILE,
@@ -25,13 +26,15 @@ _OPTION_ARGUMENT_NAME: dict[str, Union[Options, dict]] = {
25
26
  "input_recovery": Options.INPUT_RECOVERY,
26
27
  "complex_column": Options.RESOLVE_COMPLEX_FIELD,
27
28
  "merge_times": Options.TIME_MERGING,
29
+ "file_pattern": Options.FILE_PATTERN,
30
+ "replace": Options.REPLACE,
28
31
  }
29
32
 
30
33
 
31
- def add_file_argument(parser: ArgumentParser, default: Optional[Path], help_text: str) -> None:
32
- """
33
- Adds 'file' argument to the provided `parser` with the provided `help_text`.
34
- If a default is not specified, the argument is required (optional otherwise)
34
+ def add_file_argument(parser: ArgumentParser, default: Path | None, help_text: str) -> None:
35
+ """Adds 'file' argument to the provided `parser` with the provided `help_text`.
36
+
37
+ If a default is not specified, the argument is required (optional otherwise).
35
38
 
36
39
  Args:
37
40
  parser: to add the argument to
@@ -44,26 +47,26 @@ def add_file_argument(parser: ArgumentParser, default: Optional[Path], help_text
44
47
  parser.add_argument("-f", "--file", type=Path, required=True, help=help_text)
45
48
 
46
49
 
47
- def add_select_agents_argument(parser: ArgumentParser, default: list[str]) -> None:
48
- """Adds optional repeatable string argument 'agent' to given `parser`"""
49
- help_text = "Provide list of agents to extract (default=None)"
50
- parser.add_argument("-a", "--agents", nargs="*", type=str, default=default, help=help_text)
50
+ def add_select_agents_argument(parser: ArgumentParser, default_value: list[str] | None) -> None:
51
+ """Adds optional repeatable string argument 'agent' to given `parser`."""
52
+ help_text = f"Provide list of agents to extract (default={default_value})"
53
+ parser.add_argument("-a", "--agents", nargs="*", type=str, default=default_value, help=help_text)
51
54
 
52
55
 
53
- def add_logfile_argument(parser: ArgumentParser, default: Path) -> None:
54
- """Adds optional argument 'logfile' to given `parser`"""
55
- help_text = "provide logging file (default=None)"
56
- parser.add_argument("-lf", "--logfile", type=Path, default=default, help=help_text)
56
+ def add_logfile_argument(parser: ArgumentParser, default_value: Path | None) -> None:
57
+ """Adds optional argument 'logfile' to given `parser`."""
58
+ help_text = f"provide logging file (default={default_value})"
59
+ parser.add_argument("-lf", "--logfile", type=Path, default=default_value, help=help_text)
57
60
 
58
61
 
59
62
  def add_output_argument(parser: ArgumentParser, default_value, help_text: str) -> None:
60
- """Adds optional argument 'output' to given `parser` using the given `help_text` and `default_value`"""
63
+ """Adds optional argument 'output' to given `parser` using the given `help_text` and `default_value`."""
61
64
  parser.add_argument("-o", "--output", type=Path, default=default_value, help=help_text)
62
65
 
63
66
 
64
67
  def add_log_level_argument(parser: ArgumentParser, default_value: str) -> None:
65
- """Adds optional argument 'log' to given `parser`"""
66
- help_text = f"choose logging level (default: {default_value})"
68
+ """Adds optional argument 'log' to given `parser`."""
69
+ help_text = f"choose logging level (default={default_value})"
67
70
  # noinspection PyTypeChecker
68
71
  parser.add_argument(
69
72
  "-l",
@@ -75,14 +78,14 @@ def add_log_level_argument(parser: ArgumentParser, default_value: str) -> None:
75
78
  )
76
79
 
77
80
 
78
- def add_encoding_argument(parser: ArgumentParser, default_value: Optional[str], help_text: str) -> None:
81
+ def add_encoding_argument(parser: ArgumentParser, default_value: str | None, help_text: str) -> None:
79
82
  """Adds optional argument `enc` to given parser"""
80
83
  parser.add_argument("-enc", "--encoding", type=str, default=default_value, help=help_text)
81
84
 
82
85
 
83
86
  def add_single_export_argument(parser: ArgumentParser, default_value: bool) -> None:
84
- """Adds optional repeatable string argument 'agent' to given `parser`"""
85
- help_text = "Enable export of single agents (default=False)"
87
+ """Adds optional repeatable string argument 'agent' to given `parser`."""
88
+ help_text = f"Enable export of single agents (default={default_value})"
86
89
  parser.add_argument(
87
90
  "-se",
88
91
  "--single-export",
@@ -93,8 +96,8 @@ def add_single_export_argument(parser: ArgumentParser, default_value: bool) -> N
93
96
 
94
97
 
95
98
  def add_memory_saving_argument(parser: ArgumentParser, default_value: bool) -> None:
96
- """Adds optional bool argument to given `parser` to enable memory saving mode"""
97
- help_text = "Reduces memory usage profile at the cost of runtime (default=False)"
99
+ """Adds optional bool argument to given `parser` to enable memory saving mode."""
100
+ help_text = f"Reduces memory usage profile at the cost of runtime (default={default_value})"
98
101
  parser.add_argument(
99
102
  "-m",
100
103
  "--memory-saving",
@@ -104,10 +107,10 @@ def add_memory_saving_argument(parser: ArgumentParser, default_value: bool) -> N
104
107
  )
105
108
 
106
109
 
107
- def add_resolve_complex_argument(parser: ArgumentParser, default_value: Union[ResolveOptions, str]):
108
- """Instructs given `parser` how to deal with complex field outputs"""
110
+ def add_resolve_complex_argument(parser: ArgumentParser, default_value: ResolveOptions | str):
111
+ """Instructs given `parser` how to deal with complex field outputs."""
109
112
  default_value = default_value if isinstance(default_value, ResolveOptions) else ResolveOptions[default_value]
110
- help_text = f"How to deal with complex index columns? (default={default_value})"
113
+ help_text = f"How to deal with complex index columns? (default={default_value.name})"
111
114
  parser.add_argument(
112
115
  "-cc",
113
116
  "--complex-column",
@@ -118,10 +121,10 @@ def add_resolve_complex_argument(parser: ArgumentParser, default_value: Union[Re
118
121
  )
119
122
 
120
123
 
121
- def add_time_argument(parser: ArgumentParser, default_value: Union[TimeOptions, str]) -> None:
122
- """Adds optional argument to given `parser` to define conversion of TimeSteps"""
124
+ def add_time_argument(parser: ArgumentParser, default_value: TimeOptions | str) -> None:
125
+ """Adds optional argument to given `parser` to define conversion of TimeSteps."""
123
126
  default_value = default_value if isinstance(default_value, TimeOptions) else TimeOptions[default_value]
124
- help_text = "Apply conversion of time steps to given format (default=UTC)"
127
+ help_text = f"Apply conversion of time steps to given format (default={default_value.name})"
125
128
  parser.add_argument(
126
129
  "-t",
127
130
  "--time",
@@ -132,8 +135,8 @@ def add_time_argument(parser: ArgumentParser, default_value: Union[TimeOptions,
132
135
  )
133
136
 
134
137
 
135
- def add_merge_time_argument(parser: ArgumentParser, defaults: Optional[list[int]] = None) -> None:
136
- """Adds optional three-fold argument for merging of TimeSteps to given `parser`"""
138
+ def add_merge_time_argument(parser: ArgumentParser, defaults: list[int] | None = None) -> None:
139
+ """Adds optional three-fold argument for merging of TimeSteps to given `parser`."""
137
140
  if defaults is None:
138
141
  defaults = []
139
142
  if (
@@ -150,29 +153,61 @@ def add_merge_time_argument(parser: ArgumentParser, defaults: Optional[list[int]
150
153
  parser.add_argument("-mt", "--merge-times", type=int, nargs=3, default=defaults, help=help_text)
151
154
 
152
155
 
153
- def add_inputs_recovery_argument(parser: ArgumentParser, default: bool) -> None:
154
- """Adds optional bool argument to given `parser` to recover inputs"""
155
- help_text = "If --(no-)input-recovery is specified, (no) inputs will be recovered"
156
- parser.add_argument(
157
- "--input-recovery",
158
- action=BooleanOptionalAction,
159
- default=default,
160
- help=help_text,
161
- )
156
+ def add_inputs_recovery_argument(parser: ArgumentParser, default_value: bool) -> None:
157
+ """Adds optional bool argument to given `parser` to recover inputs."""
158
+ description = "(no) inputs will be recovered"
159
+ _add_optional_boolean_argument(parser, default_value, "input-recovery", description)
160
+
161
+
162
+ def _add_optional_boolean_argument(parser: ArgumentParser, default: bool, arg_name: str, description: str) -> None:
163
+ """Adds optional boolean argument to parser.
164
+
165
+ Argument named `arg_name` is added to given `parser` overwriting the provided default.
166
+ Help from argument `description` is added as help text.
167
+
168
+ Args:
169
+ parser: to add the argument to
170
+ default: of the argument
171
+ arg_name: long name of the argument, no short name allowed; will be prepended with 'no-' for negation
172
+ description: to create the help text from: "If --(no-)<arg_name> is specified, <description> (default=X)'
173
+ """
174
+ default_str = "--" + ("no-" if not default else "") + arg_name
175
+ help_text = f"If --(no-){arg_name} is specified, {description} (default={default_str})"
176
+ parser.add_argument(f"--{arg_name}", action=BooleanOptionalAction, default=default, help=help_text)
162
177
 
163
178
 
164
- def update_default_config(config: Optional[dict], default: dict) -> dict:
165
- """Returns `default` config with updated fields received from `config`"""
166
- result = copy.deepcopy(default)
167
- if config:
168
- for name, option in config.items():
179
+ def add_file_pattern_argument(parser: ArgumentParser, default_value: str | None) -> None:
180
+ """Adds argument to given `parser` to specify a file pattern; if no default provided, the argument is mandatory."""
181
+ help_text = f"Path to csv file(s) that are to be converted (default='{default_value}')"
182
+ required = not bool(default_value)
183
+ parser.add_argument("--file-pattern", "-fp", required=required, type=str, default=default_value, help=help_text)
184
+
185
+
186
+ def add_replace_argument(parser: ArgumentParser, default_value: bool) -> None:
187
+ """Adds optional bool argument to given `parser` to replace converted files."""
188
+ description = "original files will (not) be replaced"
189
+ _add_optional_boolean_argument(parser, default_value, "replace", description)
190
+
191
+
192
+ def update_default_config(overrides: dict[Options, Any] | None, defaults: dict[Options, Any]) -> dict[Options, Any]:
193
+ """Returns `defaults` with updated fields received from `overrides`.
194
+
195
+ Args:
196
+ overrides: updates to be applied to `defaults`
197
+ defaults: base values, possibly replaced by options specified in `config`
198
+
199
+ Returns:
200
+ Deep copy of given `defaults` with updates values as specified in `overrides`
201
+ """
202
+ result = copy.deepcopy(defaults)
203
+ if overrides:
204
+ for name, option in overrides.items():
169
205
  result[name] = option
170
206
  return result
171
207
 
172
208
 
173
209
  def map_namespace_to_options_dict(parsed: Namespace) -> dict[Options, Any]:
174
- """
175
- Maps given parsing results to their corresponding configuration option
210
+ """Maps given parsing results to their corresponding configuration option.
176
211
 
177
212
  Args:
178
213
  parsed: result of a parsing
@@ -183,10 +218,11 @@ def map_namespace_to_options_dict(parsed: Namespace) -> dict[Options, Any]:
183
218
  return _map_namespace_to_options(parsed, _OPTION_ARGUMENT_NAME)
184
219
 
185
220
 
186
- def _map_namespace_to_options(parsed: Namespace, names_to_options: dict[str, Enum]) -> dict[Options, Any]:
187
- """
188
- Maps given parsing results to their corresponding configuration option; elements that cannot be mapped are ignored.
189
- If a configuration option has inner elements, these well be also read and added as inner dictionary.
221
+ def _map_namespace_to_options(parsed: Namespace, names_to_options: dict[str, Options]) -> dict[Options, Any]:
222
+ """Maps given parsing results to their corresponding configuration option.
223
+
224
+ Elements that cannot be mapped are ignored.
225
+ If a configuration option has inner elements, these will be also read and added as inner dictionary.
190
226
 
191
227
  Args:
192
228
  parsed: result of a parsing
fameio/cli/reformat.py ADDED
@@ -0,0 +1,58 @@
1
+ # SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ from typing import Any
8
+
9
+ from fameio.cli.options import Options
10
+ from fameio.cli.parser import (
11
+ map_namespace_to_options_dict,
12
+ add_logfile_argument,
13
+ add_log_level_argument,
14
+ add_file_pattern_argument,
15
+ add_replace_argument,
16
+ )
17
+
18
+ CLI_DEFAULTS = {
19
+ Options.LOG_LEVEL: "WARN",
20
+ Options.LOG_FILE: None,
21
+ Options.FILE_PATTERN: None,
22
+ Options.REPLACE: False,
23
+ }
24
+
25
+
26
+ def handle_args(args: list[str], defaults: dict[Options, Any] | None = None) -> dict[Options, Any]:
27
+ """Converts given `args` and returns a configuration for the transform script.
28
+
29
+ Args:
30
+ args: list of (command line) arguments, e.g., ['-fp', 'my_file']; arg values take precedence over defaults
31
+ defaults: optional default values used for unspecified parameters; missing defaults are replaced by CLI defaults
32
+
33
+ Returns:
34
+ final configuration compiled from (given) `defaults` and given `args`
35
+ """
36
+ parser = _prepare_parser(defaults)
37
+ parsed = parser.parse_args(args)
38
+ return map_namespace_to_options_dict(parsed)
39
+
40
+
41
+ def _prepare_parser(defaults: dict[Options, Any] | None) -> argparse.ArgumentParser:
42
+ """Creates a parser with given defaults to handle `reformat` configuration arguments.
43
+
44
+ Returns:
45
+ new parser using given defaults for its arguments; if a default is not specified, hard-coded defaults are used
46
+ """
47
+ defaults = defaults if (defaults is not None) else {}
48
+ parser = argparse.ArgumentParser()
49
+ add_log_level_argument(parser, _get_default(defaults, Options.LOG_LEVEL))
50
+ add_logfile_argument(parser, _get_default(defaults, Options.LOG_FILE))
51
+ add_file_pattern_argument(parser, _get_default(defaults, Options.FILE_PATTERN))
52
+ add_replace_argument(parser, _get_default(defaults, Options.REPLACE))
53
+ return parser
54
+
55
+
56
+ def _get_default(defaults: dict, option: Options) -> Any:
57
+ """Returns default for given `option` or its cli default."""
58
+ return defaults.get(option, CLI_DEFAULTS[option])
fameio/input/__init__.py CHANGED
@@ -4,16 +4,16 @@
4
4
 
5
5
 
6
6
  class InputError(Exception):
7
- """An error that occurred while parsing any kind of input"""
7
+ """An error that occurred while preparing a fame input file."""
8
8
 
9
9
 
10
10
  class SchemaError(InputError):
11
- """An error that occurred while parsing a Schema"""
11
+ """An error that occurred while parsing a Schema."""
12
12
 
13
13
 
14
14
  class ScenarioError(InputError):
15
- """An error that occurred while parsing a Scenario"""
15
+ """An error that occurred while parsing a Scenario."""
16
16
 
17
17
 
18
18
  class YamlLoaderError(InputError):
19
- """An error that occurred while parsing a YAML file"""
19
+ """An error that occurred while parsing a YAML file."""
@@ -1,16 +1,18 @@
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
+ from __future__ import annotations
5
+
4
6
  from pathlib import Path
5
7
  from typing import Any
6
8
 
7
9
  import yaml
8
10
 
9
11
  from fameio.input import YamlLoaderError
10
- from fameio.input.resolver import PathResolver
11
12
  from fameio.input.loader.controller import LoaderController
12
13
  from fameio.input.loader.loader import FameYamlLoader
13
- from fameio.logs import log, log_critical_and_raise
14
+ from fameio.input.resolver import PathResolver
15
+ from fameio.logs import log, log_critical
14
16
 
15
17
  ALLOWED_SUFFIXES: tuple[str, ...] = (".yaml", ".yml")
16
18
 
@@ -29,9 +31,8 @@ def _include_callback(own_loader: FameYamlLoader, args: yaml.Node) -> Any:
29
31
  FameYamlLoader.add_constructor(FameYamlLoader.INCLUDE_COMMAND, _include_callback)
30
32
 
31
33
 
32
- def load_yaml(yaml_file_path: Path, path_resolver: PathResolver = PathResolver(), encoding: str = None) -> dict:
33
- """
34
- Loads the YAML file from given and returns its content as a dict
34
+ def load_yaml(yaml_file_path: Path, path_resolver: PathResolver = PathResolver(), encoding: str | None = None) -> dict:
35
+ """Loads the YAML file from given `yaml_file_path` and returns its content as a dict.
35
36
 
36
37
  Args:
37
38
  yaml_file_path: Path to the YAML file that is to be read
@@ -42,27 +43,26 @@ def load_yaml(yaml_file_path: Path, path_resolver: PathResolver = PathResolver()
42
43
  Content of the specified YAML file
43
44
 
44
45
  Raises:
45
- YamlLoaderError: if the YAML file could not be read
46
+ YamlLoaderError: if the YAML file could not be found, read, or parsed
46
47
  """
47
48
  log().info(_INFO_LOADING.format(yaml_file_path))
48
49
  _update_current_controller(path_resolver, encoding)
49
50
  return __CONTROLLERS[0].load(yaml_file_path)
50
51
 
51
52
 
52
- def _update_current_controller(path_resolver: PathResolver, encoding: str) -> None:
53
- """Updates the current LoaderController to use the given `path_resolver` and `encoding`"""
53
+ def _update_current_controller(path_resolver: PathResolver, encoding: str | None) -> None:
54
+ """Updates the current LoaderController to use the given `path_resolver` and `encoding`."""
54
55
  __CONTROLLERS[0] = LoaderController(path_resolver, encoding)
55
56
 
56
57
 
57
58
  def validate_yaml_file_suffix(yaml_file: Path) -> None:
58
- """
59
- Ensures that given file has a file suffix compatible with YAML
59
+ """Ensures that given file has a file suffix compatible with YAML.
60
60
 
61
61
  Args:
62
62
  yaml_file: that is to be checked for suffix correctness
63
63
 
64
64
  Raises:
65
- YamlLoaderError: if given file has no YAML-associated file suffix
65
+ YamlLoaderError: if given file has no YAML-associated file suffix, logged with level "CRITICAL"
66
66
  """
67
67
  if yaml_file.suffix.lower() not in ALLOWED_SUFFIXES:
68
- log_critical_and_raise(YamlLoaderError(_ERR_NO_YAML_SUFFIX.format(ALLOWED_SUFFIXES, yaml_file)))
68
+ raise log_critical(YamlLoaderError(_ERR_NO_YAML_SUFFIX.format(ALLOWED_SUFFIXES, yaml_file)))