sciform 0.32.3__py3-none-any.whl → 0.34.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.
sciform/formatting.py CHANGED
@@ -1,11 +1,15 @@
1
1
  """Main formatting functions."""
2
+ from __future__ import annotations
2
3
 
3
4
  from dataclasses import replace
4
5
  from decimal import Decimal
5
- from typing import cast
6
+ from typing import TYPE_CHECKING, cast
6
7
  from warnings import warn
7
8
 
9
+ from typing_extensions import Self
10
+
8
11
  from sciform.format_utils import (
12
+ Number,
9
13
  construct_val_unc_exp_str,
10
14
  construct_val_unc_str,
11
15
  format_num_by_top_bottom_dig,
@@ -13,9 +17,8 @@ from sciform.format_utils import (
13
17
  get_mantissa_exp_base,
14
18
  get_round_digit,
15
19
  get_val_unc_exp,
16
- get_val_unc_mantissa_exp_strs,
20
+ get_val_unc_mantissa_strs,
17
21
  get_val_unc_top_digit,
18
- latex_translate,
19
22
  round_val_unc,
20
23
  )
21
24
  from sciform.grouping import add_separators
@@ -26,10 +29,35 @@ from sciform.modes import (
26
29
  RoundModeEnum,
27
30
  SignModeEnum,
28
31
  )
29
- from sciform.rendered_options import RenderedOptions
32
+ from sciform.options.conversion import finalize_populated_options, populate_options
33
+ from sciform.output_conversion import convert_sciform_format
34
+
35
+ if TYPE_CHECKING: # pragma: no cover
36
+ from sciform.options.finalized_options import FinalizedOptions
37
+ from sciform.options.input_options import InputOptions
38
+ from sciform.options.populated_options import PopulatedOptions
39
+
40
+
41
+ def format_from_options(
42
+ value: Number,
43
+ uncertainty: Number | None = None,
44
+ /,
45
+ input_options: InputOptions | None = None,
46
+ ) -> FormattedNumber:
47
+ """Finalize options and select value of value/uncertainty formatter."""
48
+ populated_options = populate_options(input_options)
49
+ finalized_options = finalize_populated_options(populated_options)
50
+ value = Decimal(str(value))
51
+
52
+ if uncertainty is not None:
53
+ uncertainty = Decimal(str(uncertainty))
54
+ formatted_str = format_val_unc(value, uncertainty, finalized_options)
55
+ else:
56
+ formatted_str = format_num(value, finalized_options)
57
+ return FormattedNumber(formatted_str, populated_options)
30
58
 
31
59
 
32
- def format_non_finite(num: Decimal, options: RenderedOptions) -> str:
60
+ def format_non_finite(num: Decimal, options: FinalizedOptions) -> str:
33
61
  """Format non-finite numbers."""
34
62
  if num.is_nan():
35
63
  num_str = "nan"
@@ -53,8 +81,6 @@ def format_non_finite(num: Decimal, options: RenderedOptions) -> str:
53
81
  exp_mode=exp_mode,
54
82
  exp_format=options.exp_format,
55
83
  capitalize=options.capitalize,
56
- latex=options.latex,
57
- latex_trim_whitespace=True,
58
84
  superscript=options.superscript,
59
85
  extra_si_prefixes=options.extra_si_prefixes,
60
86
  extra_iec_prefixes=options.extra_iec_prefixes,
@@ -73,13 +99,10 @@ def format_non_finite(num: Decimal, options: RenderedOptions) -> str:
73
99
  else:
74
100
  result = result.lower()
75
101
 
76
- if options.latex:
77
- result = latex_translate(result)
78
-
79
102
  return result
80
103
 
81
104
 
82
- def format_num(num: Decimal, options: RenderedOptions) -> str:
105
+ def format_num(num: Decimal, options: FinalizedOptions) -> str:
83
106
  """Format a single number according to input options."""
84
107
  if not num.is_finite():
85
108
  return format_non_finite(num, options)
@@ -115,13 +138,13 @@ def format_num(num: Decimal, options: RenderedOptions) -> str:
115
138
  """
116
139
  exp_val = 0
117
140
 
118
- fill_char = options.fill_char.value
141
+ left_pad_char = options.left_pad_char.value
119
142
  mantissa_str = format_num_by_top_bottom_dig(
120
143
  mantissa_rounded.normalize(),
121
144
  options.left_pad_dec_place,
122
145
  round_digit,
123
146
  options.sign_mode,
124
- fill_char,
147
+ left_pad_char,
125
148
  )
126
149
 
127
150
  upper_separator = options.upper_separator.value
@@ -140,8 +163,6 @@ def format_num(num: Decimal, options: RenderedOptions) -> str:
140
163
  exp_mode=exp_mode,
141
164
  exp_format=options.exp_format,
142
165
  capitalize=options.capitalize,
143
- latex=options.latex,
144
- latex_trim_whitespace=False,
145
166
  superscript=options.superscript,
146
167
  extra_si_prefixes=options.extra_si_prefixes,
147
168
  extra_iec_prefixes=options.extra_iec_prefixes,
@@ -150,13 +171,10 @@ def format_num(num: Decimal, options: RenderedOptions) -> str:
150
171
 
151
172
  result = f"{mantissa_str}{exp_str}"
152
173
 
153
- if options.latex:
154
- result = latex_translate(result)
155
-
156
174
  return result
157
175
 
158
176
 
159
- def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
177
+ def format_val_unc(val: Decimal, unc: Decimal, options: FinalizedOptions) -> str:
160
178
  """Format value/uncertainty pair according to input options."""
161
179
  exp_mode = options.exp_mode
162
180
 
@@ -205,7 +223,7 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
205
223
  use_pdg_sig_figs=options.pdg_sig_figs,
206
224
  )
207
225
 
208
- exp_val, exp_driver_type = get_val_unc_exp(
226
+ exp_val = get_val_unc_exp(
209
227
  val_rounded,
210
228
  unc_rounded,
211
229
  options.exp_mode,
@@ -240,7 +258,7 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
240
258
  * With the calculated shared exponent
241
259
  * Without percent mode (percent mode for val/unc pairs is
242
260
  handled below in the scope of this function)
243
- * Without superscript, prefix, parts-per, or latex translations.
261
+ * Without superscript, prefix, or parts-per translations.
244
262
  The remaining steps rely on parsing an exponent string like
245
263
  'e+03' or similar. Such translations are handled within the
246
264
  scope of this function.
@@ -253,7 +271,6 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
253
271
  exp_mode=exp_mode,
254
272
  exp_val=exp_val,
255
273
  superscript=False,
256
- latex=False,
257
274
  exp_format=ExpFormatEnum.STANDARD,
258
275
  )
259
276
 
@@ -265,10 +282,9 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
265
282
  val_mantissa_exp_str = format_num(val_rounded, val_format_options)
266
283
  unc_mantissa_exp_str = format_num(unc_rounded, unc_format_options)
267
284
 
268
- (val_mantissa_str, unc_mantissa_str, exp_str) = get_val_unc_mantissa_exp_strs(
285
+ val_mantissa_str, unc_mantissa_str = get_val_unc_mantissa_strs(
269
286
  val_mantissa_exp_str,
270
287
  unc_mantissa_exp_str,
271
- exp_driver_type,
272
288
  )
273
289
 
274
290
  val_unc_str = construct_val_unc_str(
@@ -278,12 +294,11 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
278
294
  unc_mantissa,
279
295
  decimal_separator=options.decimal_separator,
280
296
  paren_uncertainty=options.paren_uncertainty,
281
- latex=options.latex,
282
297
  pm_whitespace=options.pm_whitespace,
283
298
  paren_uncertainty_separators=options.paren_uncertainty_separators,
284
299
  )
285
300
 
286
- if exp_str is not None:
301
+ if val.is_finite() or unc.is_finite() or options.nan_inf_exp:
287
302
  val_unc_exp_str = construct_val_unc_exp_str(
288
303
  val_unc_str=val_unc_str,
289
304
  exp_val=exp_val,
@@ -293,7 +308,6 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
293
308
  extra_iec_prefixes=options.extra_iec_prefixes,
294
309
  extra_parts_per_forms=options.extra_parts_per_forms,
295
310
  capitalize=options.capitalize,
296
- latex=options.latex,
297
311
  superscript=options.superscript,
298
312
  paren_uncertainty=options.paren_uncertainty,
299
313
  )
@@ -303,7 +317,62 @@ def format_val_unc(val: Decimal, unc: Decimal, options: RenderedOptions) -> str:
303
317
  if options.exp_mode is ExpModeEnum.PERCENT:
304
318
  val_unc_exp_str = f"({val_unc_exp_str})%"
305
319
 
306
- if options.latex:
307
- val_unc_exp_str = latex_translate(val_unc_exp_str)
308
-
309
320
  return val_unc_exp_str
321
+
322
+
323
+ class FormattedNumber(str):
324
+ """
325
+ Representation of a formatted value of value/uncertainty pair.
326
+
327
+ The :class:`FormattedNumber` class is returned by ``sciform``
328
+ formatting methods. In most cases it behaves like a regular python
329
+ string, but it provides functionality for post-converting the string
330
+ to various other formats such as latex or html. This allows the
331
+ formatted number to be displayed in a range of contexts other than
332
+ e.g. text terminals.
333
+
334
+ The :class:`FormattedNumber` class should never be instantiated
335
+ directly.
336
+ """
337
+
338
+ __slots__ = {
339
+ "populated_options": "Record of the :class:`PopulatedOptions` used to "
340
+ "generate the :class:`FormattedNumber`.",
341
+ }
342
+
343
+ def __new__(
344
+ cls: type[Self],
345
+ formatted_str: str,
346
+ populated_options: PopulatedOptions,
347
+ ) -> Self:
348
+ """Get a new string."""
349
+ obj = super().__new__(cls, formatted_str)
350
+ obj.populated_options = populated_options
351
+ return obj
352
+
353
+ def as_str(self: FormattedNumber) -> str:
354
+ """Return the string representation of the formatted number."""
355
+ return self.__str__()
356
+
357
+ def as_ascii(self: FormattedNumber) -> str:
358
+ """Return the ascii representation of the formatted number."""
359
+ return convert_sciform_format(self, "ascii")
360
+
361
+ def as_html(self: FormattedNumber) -> str:
362
+ """Return the html representation of the formatted number."""
363
+ return convert_sciform_format(self, "html")
364
+
365
+ def as_latex(self: FormattedNumber, *, strip_math_mode: bool = False) -> str:
366
+ """Return the latex representation of the formatted number."""
367
+ latex_repr = convert_sciform_format(self, "latex")
368
+ if strip_math_mode:
369
+ latex_repr = latex_repr.strip("$")
370
+ return latex_repr
371
+
372
+ def _repr_html_(self: FormattedNumber) -> str:
373
+ """Hook for HTML display.""" # noqa: D401
374
+ return self.as_html()
375
+
376
+ def _repr_latex_(self: FormattedNumber) -> str:
377
+ """Hook for LaTeX display.""" # noqa: D401
378
+ return self.as_latex()
sciform/fsml.py CHANGED
@@ -4,11 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
 
7
- from sciform.user_options import UserOptions
7
+ from sciform.options.input_options import InputOptions
8
8
 
9
9
  pattern = re.compile(
10
10
  r"""^
11
- (?:(?P<fill_char>[ 0])=)?
11
+ (?:(?P<left_pad_char>[ 0])=)?
12
12
  (?P<sign_mode>[-+ ])?
13
13
  (?P<alternate_mode>\#)?
14
14
  (?P<left_pad_dec_place>\d+)?
@@ -51,14 +51,14 @@ def parse_exp_mode(
51
51
  return exp_mode, capitalize
52
52
 
53
53
 
54
- def format_options_from_fmt_spec(fmt_spec: str) -> UserOptions:
55
- """Resolve UserOptions form format specification string."""
54
+ def format_options_from_fmt_spec(fmt_spec: str) -> InputOptions:
55
+ """Resolve InputOptions from format specification string."""
56
56
  match = pattern.match(fmt_spec)
57
57
  if match is None:
58
58
  msg = f"Invalid format specifier: '{fmt_spec}'"
59
59
  raise ValueError(msg)
60
60
 
61
- fill_char = match.group("fill_char")
61
+ left_pad_char = match.group("left_pad_char")
62
62
  sign_mode = match.group("sign_mode")
63
63
 
64
64
  alternate_mode = match.group("alternate_mode")
@@ -101,8 +101,8 @@ def format_options_from_fmt_spec(fmt_spec: str) -> UserOptions:
101
101
  else:
102
102
  paren_uncertainty = None
103
103
 
104
- return UserOptions(
105
- fill_char=fill_char,
104
+ return InputOptions(
105
+ left_pad_char=left_pad_char,
106
106
  sign_mode=sign_mode,
107
107
  left_pad_dec_place=left_pad_dec_place,
108
108
  round_mode=round_mode,
@@ -4,21 +4,28 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from sciform import global_options, modes
8
- from sciform.user_options import UserOptions
7
+ from sciform.options import global_options
8
+ from sciform.options.conversion import populate_options
9
+ from sciform.options.input_options import InputOptions
9
10
 
10
11
  if TYPE_CHECKING: # pragma: no cover
11
12
  from types import TracebackType
12
13
 
13
- from sciform.rendered_options import RenderedOptions
14
+ from sciform import modes
15
+ from sciform.options.populated_options import PopulatedOptions
14
16
 
15
17
 
16
- def print_global_defaults() -> None:
17
- """Print current global default formatting options as a dictionary."""
18
- print(str(global_options.GLOBAL_DEFAULT_OPTIONS)) # noqa: T201
18
+ def get_default_global_options() -> PopulatedOptions:
19
+ """Return the package default global options."""
20
+ return global_options.PKG_DEFAULT_OPTIONS
19
21
 
20
22
 
21
- def set_global_defaults( # noqa: PLR0913
23
+ def get_global_options() -> PopulatedOptions:
24
+ """Return the current global options."""
25
+ return global_options.GLOBAL_DEFAULT_OPTIONS
26
+
27
+
28
+ def set_global_options( # noqa: PLR0913
22
29
  *,
23
30
  exp_mode: modes.ExpMode | None = None,
24
31
  exp_val: int | type(modes.AutoExpVal) | None = None,
@@ -28,7 +35,7 @@ def set_global_defaults( # noqa: PLR0913
28
35
  decimal_separator: modes.DecimalSeparators | None = None,
29
36
  lower_separator: modes.LowerSeparators | None = None,
30
37
  sign_mode: modes.SignMode | None = None,
31
- fill_char: modes.FillChar | None = None,
38
+ left_pad_char: modes.LeftPadChar | None = None,
32
39
  left_pad_dec_place: int | None = None,
33
40
  exp_format: modes.ExpFormat | None = None,
34
41
  extra_si_prefixes: dict[int, str] | None = None,
@@ -36,7 +43,6 @@ def set_global_defaults( # noqa: PLR0913
36
43
  extra_parts_per_forms: dict[int, str] | None = None,
37
44
  capitalize: bool | None = None,
38
45
  superscript: bool | None = None,
39
- latex: bool | None = None,
40
46
  nan_inf_exp: bool | None = None,
41
47
  paren_uncertainty: bool | None = None,
42
48
  pdg_sig_figs: bool | None = None,
@@ -48,11 +54,11 @@ def set_global_defaults( # noqa: PLR0913
48
54
  add_ppth_form: bool = False,
49
55
  ) -> None:
50
56
  """
51
- Configure global default format options.
57
+ Configure the global options.
52
58
 
53
59
  Accepts the same keyword arguments as :class:`Formatter`.
54
60
  """
55
- user_options = UserOptions(
61
+ input_options = InputOptions(
56
62
  exp_mode=exp_mode,
57
63
  exp_val=exp_val,
58
64
  round_mode=round_mode,
@@ -61,7 +67,7 @@ def set_global_defaults( # noqa: PLR0913
61
67
  decimal_separator=decimal_separator,
62
68
  lower_separator=lower_separator,
63
69
  sign_mode=sign_mode,
64
- fill_char=fill_char,
70
+ left_pad_char=left_pad_char,
65
71
  left_pad_dec_place=left_pad_dec_place,
66
72
  exp_format=exp_format,
67
73
  extra_si_prefixes=extra_si_prefixes,
@@ -69,7 +75,6 @@ def set_global_defaults( # noqa: PLR0913
69
75
  extra_parts_per_forms=extra_parts_per_forms,
70
76
  capitalize=capitalize,
71
77
  superscript=superscript,
72
- latex=latex,
73
78
  nan_inf_exp=nan_inf_exp,
74
79
  paren_uncertainty=paren_uncertainty,
75
80
  pdg_sig_figs=pdg_sig_figs,
@@ -80,30 +85,30 @@ def set_global_defaults( # noqa: PLR0913
80
85
  add_small_si_prefixes=add_small_si_prefixes,
81
86
  add_ppth_form=add_ppth_form,
82
87
  )
83
- set_global_defaults_rendered(user_options.render())
88
+ set_global_options_populated(populate_options(input_options))
84
89
 
85
90
 
86
- def set_global_defaults_rendered(rendered_options: RenderedOptions) -> None:
87
- """Directly set global defaults to input RenderedOptions."""
88
- global_options.GLOBAL_DEFAULT_OPTIONS = rendered_options
91
+ def set_global_options_populated(populated_options: PopulatedOptions) -> None:
92
+ """Directly set global options to input :class:`PopulatedOptions`."""
93
+ global_options.GLOBAL_DEFAULT_OPTIONS = populated_options
89
94
 
90
95
 
91
- def reset_global_defaults() -> None:
92
- """Reset global default options to :mod:`sciform` package defaults."""
96
+ def reset_global_options() -> None:
97
+ """Reset global options to :mod:`sciform` package defaults."""
93
98
  global_options.GLOBAL_DEFAULT_OPTIONS = global_options.PKG_DEFAULT_OPTIONS
94
99
 
95
100
 
96
- class GlobalDefaultsContext:
101
+ class GlobalOptionsContext:
97
102
  """
98
- Temporarily update global default options.
103
+ Temporarily update global options.
99
104
 
100
- New settings are applied when the context is entered and original
101
- global settings are re-applied when the context is exited. Accepts
102
- the same keyword arguments as :class:`Formatter`.
105
+ New global options are applied when the context is entered and the
106
+ original global settings are re-applied when the context is exited.
107
+ Accepts the same keyword arguments as :class:`Formatter`.
103
108
  """
104
109
 
105
110
  def __init__( # noqa: PLR0913
106
- self: GlobalDefaultsContext,
111
+ self: GlobalOptionsContext,
107
112
  *,
108
113
  exp_mode: modes.ExpMode | None = None,
109
114
  exp_val: int | type(modes.AutoExpVal) | None = None,
@@ -113,7 +118,7 @@ class GlobalDefaultsContext:
113
118
  decimal_separator: modes.DecimalSeparators | None = None,
114
119
  lower_separator: modes.LowerSeparators | None = None,
115
120
  sign_mode: modes.SignMode | None = None,
116
- fill_char: modes.FillChar | None = None,
121
+ left_pad_char: modes.LeftPadChar | None = None,
117
122
  left_pad_dec_place: int | None = None,
118
123
  exp_format: modes.ExpFormat | None = None,
119
124
  extra_si_prefixes: dict[int, str] | None = None,
@@ -121,7 +126,6 @@ class GlobalDefaultsContext:
121
126
  extra_parts_per_forms: dict[int, str] | None = None,
122
127
  capitalize: bool | None = None,
123
128
  superscript: bool | None = None,
124
- latex: bool | None = None,
125
129
  nan_inf_exp: bool | None = None,
126
130
  paren_uncertainty: bool | None = None,
127
131
  pdg_sig_figs: bool | None = None,
@@ -132,7 +136,7 @@ class GlobalDefaultsContext:
132
136
  add_small_si_prefixes: bool = False,
133
137
  add_ppth_form: bool = False,
134
138
  ) -> None:
135
- user_options = UserOptions(
139
+ input_options = InputOptions(
136
140
  exp_mode=exp_mode,
137
141
  exp_val=exp_val,
138
142
  round_mode=round_mode,
@@ -141,7 +145,7 @@ class GlobalDefaultsContext:
141
145
  decimal_separator=decimal_separator,
142
146
  lower_separator=lower_separator,
143
147
  sign_mode=sign_mode,
144
- fill_char=fill_char,
148
+ left_pad_char=left_pad_char,
145
149
  left_pad_dec_place=left_pad_dec_place,
146
150
  exp_format=exp_format,
147
151
  extra_si_prefixes=extra_si_prefixes,
@@ -149,7 +153,6 @@ class GlobalDefaultsContext:
149
153
  extra_parts_per_forms=extra_parts_per_forms,
150
154
  capitalize=capitalize,
151
155
  superscript=superscript,
152
- latex=latex,
153
156
  nan_inf_exp=nan_inf_exp,
154
157
  paren_uncertainty=paren_uncertainty,
155
158
  pdg_sig_figs=pdg_sig_figs,
@@ -160,19 +163,19 @@ class GlobalDefaultsContext:
160
163
  add_small_si_prefixes=add_small_si_prefixes,
161
164
  add_ppth_form=add_ppth_form,
162
165
  )
163
- self.rendered_options = user_options.render()
166
+ self.populated_options = populate_options(input_options)
164
167
  self.initial_global_defaults = None
165
168
 
166
- def __enter__(self: GlobalDefaultsContext) -> None:
169
+ def __enter__(self: GlobalOptionsContext) -> None:
167
170
  """Enter the context."""
168
- self.initial_global_defaults = global_options.GLOBAL_DEFAULT_OPTIONS
169
- set_global_defaults_rendered(self.rendered_options)
171
+ self.initial_global_defaults = get_global_options()
172
+ set_global_options_populated(self.populated_options)
170
173
 
171
174
  def __exit__(
172
- self: GlobalDefaultsContext,
175
+ self: GlobalOptionsContext,
173
176
  exc_type: type[BaseException] | None,
174
177
  exc_val: BaseException | None,
175
178
  exc_tb: TracebackType | None,
176
179
  ) -> None:
177
180
  """Exit the context."""
178
- set_global_defaults_rendered(self.initial_global_defaults)
181
+ set_global_options_populated(self.initial_global_defaults)
sciform/modes.py CHANGED
@@ -50,11 +50,11 @@ class AutoDigits(metaclass=SentinelMeta):
50
50
  """
51
51
 
52
52
 
53
- FillChar = Literal[" ", "0"]
53
+ LeftPadChar = Literal[" ", "0"]
54
54
 
55
55
 
56
- class FillCharEnum(str, Enum):
57
- """Fill mode Enum."""
56
+ class LeftPadCharEnum(str, Enum):
57
+ """Left pad character mode Enum."""
58
58
 
59
59
  SPACE = " "
60
60
  ZERO = "0"
@@ -145,13 +145,6 @@ class ExpFormatEnum(str, Enum):
145
145
  PARTS_PER = "parts_per"
146
146
 
147
147
 
148
- class ExpDriver(Enum):
149
- """Exponent drive Enum. Used for value/uncertainty formatting."""
150
-
151
- VAL = "val"
152
- UNC = "unc"
153
-
154
-
155
148
  T = TypeVar("T", bound=Enum)
156
149
 
157
150
 
@@ -0,0 +1 @@
1
+ """Code pertaining to sciform options handling."""
@@ -0,0 +1,120 @@
1
+ """Code for converting between various types of sciform options."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict
6
+ from typing import TYPE_CHECKING
7
+
8
+ from sciform import modes
9
+ from sciform.options import global_options
10
+ from sciform.options.finalized_options import FinalizedOptions
11
+ from sciform.options.populated_options import PopulatedOptions
12
+ from sciform.options.validation import validate_options
13
+
14
+ if TYPE_CHECKING: # pragma: no cover
15
+ from sciform.options.input_options import InputOptions
16
+
17
+
18
+ def populate_extra_si_prefixes(
19
+ extra_si_prefixes: dict[int, str] | None,
20
+ *,
21
+ add_c_prefix: bool,
22
+ add_small_si_prefixes: bool,
23
+ ) -> dict[int, str]:
24
+ """Populate extra_si_prefixes dict."""
25
+ if add_c_prefix:
26
+ if extra_si_prefixes is None:
27
+ extra_si_prefixes = {}
28
+ if -2 not in extra_si_prefixes:
29
+ extra_si_prefixes[-2] = "c"
30
+ if add_small_si_prefixes:
31
+ if extra_si_prefixes is None:
32
+ extra_si_prefixes = {}
33
+ if -2 not in extra_si_prefixes:
34
+ extra_si_prefixes[-2] = "c"
35
+ if -1 not in extra_si_prefixes:
36
+ extra_si_prefixes[-1] = "d"
37
+ if +1 not in extra_si_prefixes:
38
+ extra_si_prefixes[+1] = "da"
39
+ if +2 not in extra_si_prefixes:
40
+ extra_si_prefixes[+2] = "h"
41
+ return extra_si_prefixes
42
+
43
+
44
+ def populate_extra_parts_per_forms(
45
+ extra_parts_per_forms: dict[int, str] | None,
46
+ *,
47
+ add_ppth_form: bool,
48
+ ) -> dict[int, str]:
49
+ """Populate extra_si_prefixes dict."""
50
+ if add_ppth_form:
51
+ if extra_parts_per_forms is None:
52
+ extra_parts_per_forms = {}
53
+ if -3 not in extra_parts_per_forms:
54
+ extra_parts_per_forms[-3] = "ppth"
55
+ return extra_parts_per_forms
56
+
57
+
58
+ def populate_options(input_options: InputOptions) -> PopulatedOptions:
59
+ """Populate InputOptions into PopulatedOptions."""
60
+ global_options_dict = asdict(global_options.GLOBAL_DEFAULT_OPTIONS)
61
+ input_options_dict = asdict(input_options)
62
+ kwargs = {}
63
+ for key in list(input_options_dict.keys()):
64
+ if key in ["add_c_prefix", "add_small_si_prefixes", "add_ppth_form"]:
65
+ continue
66
+
67
+ value = input_options_dict[key]
68
+ if key == "left_pad_char" and value == 0:
69
+ value = "0"
70
+
71
+ if key == "extra_si_prefixes":
72
+ value = populate_extra_si_prefixes(
73
+ value,
74
+ add_c_prefix=input_options.add_c_prefix,
75
+ add_small_si_prefixes=input_options.add_small_si_prefixes,
76
+ )
77
+ if key == "extra_parts_per_forms" and input_options.add_ppth_form:
78
+ value = populate_extra_parts_per_forms(
79
+ value,
80
+ add_ppth_form=input_options.add_ppth_form,
81
+ )
82
+
83
+ if value is None:
84
+ populated_value = global_options_dict[key]
85
+ else:
86
+ populated_value = value
87
+
88
+ kwargs[key] = populated_value
89
+ populated_options = PopulatedOptions(**kwargs)
90
+ validate_options(populated_options)
91
+ return populated_options
92
+
93
+
94
+ key_to_enum_dict = {
95
+ "exp_mode": modes.ExpModeEnum,
96
+ "round_mode": modes.RoundModeEnum,
97
+ "upper_separator": modes.SeparatorEnum,
98
+ "decimal_separator": modes.SeparatorEnum,
99
+ "lower_separator": modes.SeparatorEnum,
100
+ "sign_mode": modes.SignModeEnum,
101
+ "left_pad_char": modes.LeftPadCharEnum,
102
+ "exp_format": modes.ExpFormatEnum,
103
+ }
104
+
105
+
106
+ def finalize_populated_options(populated_options: PopulatedOptions) -> FinalizedOptions:
107
+ """Convert PopulatedOptions into FinalizedOptions with enum values."""
108
+ kwargs = populated_options.as_dict()
109
+ for key, value in kwargs.items():
110
+ if key in key_to_enum_dict:
111
+ enum = key_to_enum_dict[key]
112
+ kwargs[key] = modes.mode_str_to_enum(value, enum)
113
+ return FinalizedOptions(**kwargs)
114
+
115
+
116
+ def finalize_input_options(input_options: InputOptions) -> FinalizedOptions:
117
+ """Convert InputOptions into FinalizedOptions."""
118
+ populated_options = populate_options(input_options)
119
+ finalized_options = finalize_populated_options(populated_options)
120
+ return finalized_options