sciform 0.36.0__py3-none-any.whl → 0.38.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/__init__.py CHANGED
@@ -11,7 +11,6 @@ from sciform.api.global_configuration import (
11
11
  )
12
12
  from sciform.api.scinum import SciNum
13
13
  from sciform.options.input_options import InputOptions
14
- from sciform.options.option_types import AutoDigits, AutoExpVal
15
14
  from sciform.options.populated_options import PopulatedOptions
16
15
 
17
16
  __all__ = [
@@ -22,8 +21,6 @@ __all__ = [
22
21
  "get_global_options",
23
22
  "reset_global_options",
24
23
  "set_global_options",
25
- "AutoDigits",
26
- "AutoExpVal",
27
24
  "SciNum",
28
25
  "InputOptions",
29
26
  "PopulatedOptions",
sciform/api/formatter.py CHANGED
@@ -107,7 +107,7 @@ class Formatter:
107
107
  >>> print(formatter.populated_options)
108
108
  PopulatedOptions(
109
109
  'exp_mode': 'engineering',
110
- 'exp_val': AutoExpVal,
110
+ 'exp_val': 'auto',
111
111
  'round_mode': 'sig_fig',
112
112
  'ndigits': 2,
113
113
  'upper_separator': '',
@@ -124,7 +124,6 @@ class Formatter:
124
124
  'superscript': True,
125
125
  'nan_inf_exp': False,
126
126
  'paren_uncertainty': False,
127
- 'pdg_sig_figs': False,
128
127
  'left_pad_matching': False,
129
128
  'paren_uncertainty_trim': True,
130
129
  'pm_whitespace': True,
@@ -143,9 +142,9 @@ class Formatter:
143
142
  self: Formatter,
144
143
  *,
145
144
  exp_mode: option_types.ExpMode | None = None,
146
- exp_val: int | type(option_types.AutoExpVal) | None = None,
145
+ exp_val: int | option_types.ExpVal | None = None,
147
146
  round_mode: option_types.RoundMode | None = None,
148
- ndigits: int | type(option_types.AutoDigits) | None = None,
147
+ ndigits: int | None = None,
149
148
  upper_separator: option_types.UpperSeparators | None = None,
150
149
  decimal_separator: option_types.DecimalSeparators | None = None,
151
150
  lower_separator: option_types.LowerSeparators | None = None,
@@ -160,7 +159,6 @@ class Formatter:
160
159
  superscript: bool | None = None,
161
160
  nan_inf_exp: bool | None = None,
162
161
  paren_uncertainty: bool | None = None,
163
- pdg_sig_figs: bool | None = None,
164
162
  left_pad_matching: bool | None = None,
165
163
  paren_uncertainty_trim: bool | None = None,
166
164
  pm_whitespace: bool | None = None,
@@ -174,10 +172,10 @@ class Formatter:
174
172
  The following checks are performed when creating a new
175
173
  :class:`Formatter` object:
176
174
 
177
- * ``ndigits`` >= 1 for significant figure rounding mode
175
+ * If ``round_mode`` is ``"sig_fig"`` then ``ndigits`` >= 1.
178
176
  * ``exp_val`` must be consistent with the exponent mode. If
179
177
  ``exp_val`` is specified (i.e. not ``None``) and ``exp_val``
180
- is not ``AutoExpVal`` then
178
+ is not ``"auto"`` then
181
179
 
182
180
  * ``exp_val`` must be 0 for fixed point and percent modes
183
181
  * ``exp_val`` must be a multiple of 3 for engineering and
@@ -189,7 +187,7 @@ class Formatter:
189
187
  * ``decimal_separator`` may be any of ``['.', ',']``
190
188
  * ``lower_separator`` may be any of ``['', ' ', '_']``
191
189
 
192
- :param exp_mode: Specify the formatting mode.
190
+ :param exp_mode: Specify the exponent formatting mode.
193
191
  :type exp_mode: ``Literal['fixed_point', 'percent',
194
192
  'scientific', 'engineering', 'engineering_shifted', 'binary',
195
193
  'binary_iec'] | None``
@@ -197,16 +195,14 @@ class Formatter:
197
195
  chosen. If an integer is specified, the value must be 0 for
198
196
  fixed point and percent modes, an integer multiple of 3 for
199
197
  engineering and engineering shifted modes, and an integer
200
- multiple of 10 for binary IEC mode.
201
- :type exp_val: ``int | type[AutoExpVal] | None``
198
+ multiple of 10 for binary IEC mode. Can be set to ``"auto"``.
199
+ :type exp_val: ``int | Literal['auto'] | None``
202
200
  :param round_mode: Indicate how to round numbers during
203
201
  formatting.
204
- :type round_mode: ``Literal['sig_fig', 'dec_place'] | None``
205
- :param ndigits: Indicate how the many significant digits or the
206
- decimal place to use for rounding. Must be >= 1 for
207
- significant figure rounding. Can be any integer for decimal
208
- place rounding.
209
- :type ndigits: ``int | type[AutoDigits] | None``
202
+ :type round_mode: ``Literal['sig_fig', 'dec_place', 'all', 'pdg'] | None``
203
+ :param ndigits: Specifies how many digits to use for significant figure
204
+ or digits-past-the-decimal rounding.
205
+ :type ndigits: ``int | None``
210
206
  :param upper_separator: Separator character to be used to group
211
207
  digits above the decimal symbol.
212
208
  :type upper_separator: ``Literal['', ',', '.', ' ', '_'] | None``
@@ -261,11 +257,6 @@ class Formatter:
261
257
  uncertainty mode (e.g. ``12.34(82)`` instead of
262
258
  ``12.34 ± 0.82``) should be used.
263
259
  :type paren_uncertainty: ``bool | None``
264
- :param pdg_sig_figs: Flag indicating whether the
265
- particle-data-group conventions should be used to
266
- automatically determine the number of significant figures to
267
- use for uncertainty. Ignored for single value formatting.
268
- :type pdg_sig_figs: ``bool | None``
269
260
  :param left_pad_matching: Flag indicating if the value or
270
261
  uncertainty should be left padded to ensure they are both left
271
262
  padded to the same digits place.
@@ -312,7 +303,6 @@ class Formatter:
312
303
  superscript=superscript,
313
304
  nan_inf_exp=nan_inf_exp,
314
305
  paren_uncertainty=paren_uncertainty,
315
- pdg_sig_figs=pdg_sig_figs,
316
306
  left_pad_matching=left_pad_matching,
317
307
  paren_uncertainty_trim=paren_uncertainty_trim,
318
308
  pm_whitespace=pm_whitespace,
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Literal
6
6
 
7
7
  from sciform.options import global_options, option_types
8
8
  from sciform.options.conversion import populate_options
@@ -27,9 +27,9 @@ def get_global_options() -> PopulatedOptions:
27
27
  def set_global_options( # noqa: PLR0913
28
28
  *,
29
29
  exp_mode: option_types.ExpMode | None = None,
30
- exp_val: int | type(option_types.AutoExpVal) | None = None,
30
+ exp_val: int | Literal["auto"] | None = None,
31
31
  round_mode: option_types.RoundMode | None = None,
32
- ndigits: int | type(option_types.AutoDigits) | None = None,
32
+ ndigits: int | Literal["all", "pdg"] | None = None,
33
33
  upper_separator: option_types.UpperSeparators | None = None,
34
34
  decimal_separator: option_types.DecimalSeparators | None = None,
35
35
  lower_separator: option_types.LowerSeparators | None = None,
@@ -44,7 +44,6 @@ def set_global_options( # noqa: PLR0913
44
44
  superscript: bool | None = None,
45
45
  nan_inf_exp: bool | None = None,
46
46
  paren_uncertainty: bool | None = None,
47
- pdg_sig_figs: bool | None = None,
48
47
  left_pad_matching: bool | None = None,
49
48
  paren_uncertainty_trim: bool | None = None,
50
49
  pm_whitespace: bool | None = None,
@@ -76,7 +75,6 @@ def set_global_options( # noqa: PLR0913
76
75
  superscript=superscript,
77
76
  nan_inf_exp=nan_inf_exp,
78
77
  paren_uncertainty=paren_uncertainty,
79
- pdg_sig_figs=pdg_sig_figs,
80
78
  left_pad_matching=left_pad_matching,
81
79
  paren_uncertainty_trim=paren_uncertainty_trim,
82
80
  pm_whitespace=pm_whitespace,
@@ -110,9 +108,9 @@ class GlobalOptionsContext:
110
108
  self: GlobalOptionsContext,
111
109
  *,
112
110
  exp_mode: option_types.ExpMode | None = None,
113
- exp_val: int | type(option_types.AutoExpVal) | None = None,
111
+ exp_val: int | option_types.ExpVal | None = None,
114
112
  round_mode: option_types.RoundMode | None = None,
115
- ndigits: int | type(option_types.AutoDigits) | None = None,
113
+ ndigits: int | option_types.NDigits | None = None,
116
114
  upper_separator: option_types.UpperSeparators | None = None,
117
115
  decimal_separator: option_types.DecimalSeparators | None = None,
118
116
  lower_separator: option_types.LowerSeparators | None = None,
@@ -127,7 +125,6 @@ class GlobalOptionsContext:
127
125
  superscript: bool | None = None,
128
126
  nan_inf_exp: bool | None = None,
129
127
  paren_uncertainty: bool | None = None,
130
- pdg_sig_figs: bool | None = None,
131
128
  left_pad_matching: bool | None = None,
132
129
  paren_uncertainty_trim: bool | None = None,
133
130
  pm_whitespace: bool | None = None,
@@ -154,7 +151,6 @@ class GlobalOptionsContext:
154
151
  superscript=superscript,
155
152
  nan_inf_exp=nan_inf_exp,
156
153
  paren_uncertainty=paren_uncertainty,
157
- pdg_sig_figs=pdg_sig_figs,
158
154
  left_pad_matching=left_pad_matching,
159
155
  paren_uncertainty_trim=paren_uncertainty_trim,
160
156
  pm_whitespace=pm_whitespace,
@@ -15,13 +15,12 @@ from sciform.format_utils.numbers import (
15
15
  from sciform.options.option_types import (
16
16
  ExpFormatEnum,
17
17
  ExpModeEnum,
18
+ ExpValEnum,
18
19
  )
19
20
 
20
21
  if TYPE_CHECKING: # pragma: no cover
21
22
  from decimal import Decimal
22
23
 
23
- from sciform.options.option_types import AutoExpVal
24
-
25
24
 
26
25
  def get_translation_dict(
27
26
  exp_format: ExpFormatEnum,
@@ -120,7 +119,7 @@ def get_val_unc_exp(
120
119
  val: Decimal,
121
120
  unc: Decimal,
122
121
  exp_mode: ExpModeEnum,
123
- input_exp: int | AutoExpVal,
122
+ input_exp: int | ExpValEnum,
124
123
  ) -> int:
125
124
  """Get exponent for value/uncertainty formatting."""
126
125
  if val.is_finite() and unc.is_finite():
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import decimal
6
- from typing import TYPE_CHECKING
6
+ from decimal import Decimal
7
7
 
8
8
  from sciform.format_utils.numbers import (
9
9
  get_top_dec_place,
@@ -14,9 +14,6 @@ from sciform.options.option_types import (
14
14
  SignModeEnum,
15
15
  )
16
16
 
17
- if TYPE_CHECKING: # pragma: no cover
18
- from decimal import Decimal
19
-
20
17
 
21
18
  def get_sign_str(num: Decimal, sign_mode: SignModeEnum) -> str:
22
19
  """Get the format sign string."""
@@ -95,11 +92,26 @@ def construct_num_str(
95
92
  return f"{sign_str}{pad_str}{abs_num_str}"
96
93
 
97
94
 
95
+ def parse_mantissa_str_to_dec(
96
+ mantissa_str: str,
97
+ decimal_separator: DecimalSeparatorEnums,
98
+ ) -> Decimal:
99
+ """Convert a string, possibly with non-standard separators, to a decimal."""
100
+ clean_mantissa_str = mantissa_str
101
+ for separator in SeparatorEnum:
102
+ if separator != decimal_separator:
103
+ clean_mantissa_str = clean_mantissa_str.replace(
104
+ separator,
105
+ "",
106
+ )
107
+ clean_mantissa_str = clean_mantissa_str.replace(decimal_separator, ".")
108
+ mantissa_dec = Decimal(clean_mantissa_str)
109
+ return mantissa_dec
110
+
111
+
98
112
  def construct_val_unc_str( # noqa: PLR0913
99
113
  val_mantissa_str: str,
100
114
  unc_mantissa_str: str,
101
- val_mantissa: Decimal,
102
- unc_mantissa: Decimal,
103
115
  decimal_separator: DecimalSeparatorEnums,
104
116
  *,
105
117
  paren_uncertainty: bool,
@@ -113,22 +125,20 @@ def construct_val_unc_str( # noqa: PLR0913
113
125
  pm_symb = f" {pm_symb} "
114
126
  val_unc_str = f"{val_mantissa_str}{pm_symb}{unc_mantissa_str}"
115
127
  else:
116
- if (
117
- paren_uncertainty_trim
118
- and unc_mantissa.is_finite()
119
- and val_mantissa.is_finite()
120
- and 0 < unc_mantissa < abs(val_mantissa)
121
- ):
122
- """
123
- Don't strip the unc_mantissa_str if val_mantissa is non-finite.
124
- Don't strip the unc_mantissa_str if unc_mantissa == 0 (because then the
125
- empty string would remain).
126
- Don't left strip the unc_mantissa_str if unc_mantissa >= val_mantissa
127
- """
128
- for separator in SeparatorEnum:
129
- if separator != decimal_separator:
130
- unc_mantissa_str = unc_mantissa_str.replace(separator, "")
131
- unc_mantissa_str = unc_mantissa_str.lstrip("0" + decimal_separator)
128
+ if paren_uncertainty_trim:
129
+ val_dec = parse_mantissa_str_to_dec(val_mantissa_str, decimal_separator)
130
+ unc_dec = parse_mantissa_str_to_dec(unc_mantissa_str, decimal_separator)
131
+ if unc_dec.is_finite() and val_dec.is_finite():
132
+ if unc_dec == 0:
133
+ unc_mantissa_str = "0"
134
+ elif unc_dec < abs(val_dec):
135
+ for separator in SeparatorEnum:
136
+ if separator != decimal_separator:
137
+ unc_mantissa_str = unc_mantissa_str.replace(
138
+ separator,
139
+ "",
140
+ )
141
+ unc_mantissa_str = unc_mantissa_str.lstrip("0" + decimal_separator)
132
142
  val_unc_str = f"{val_mantissa_str}({unc_mantissa_str})"
133
143
  return val_unc_str
134
144
 
@@ -13,15 +13,14 @@ from sciform.formatting.parser import (
13
13
  non_finite_val_pattern,
14
14
  )
15
15
  from sciform.options.option_types import (
16
- AutoDigits,
17
- AutoExpVal,
18
16
  ExpModeEnum,
17
+ ExpValEnum,
19
18
  )
20
19
 
21
20
 
22
21
  def get_top_dec_place(num: Decimal) -> int:
23
22
  """Get the decimal place of a decimal's most significant digit."""
24
- if not num.is_finite() or num == 0:
23
+ if not num.is_finite():
25
24
  return 0
26
25
  _, digits, exp = num.normalize().as_tuple()
27
26
  return len(digits) + exp - 1
@@ -48,7 +47,7 @@ def get_val_unc_top_dec_place(
48
47
  input_top_dec_place: int,
49
48
  *,
50
49
  left_pad_matching: bool,
51
- ) -> int | AutoDigits:
50
+ ) -> int:
52
51
  """Get top decimal place for value/uncertainty formatting."""
53
52
  if left_pad_matching:
54
53
  val_top_dec_place = get_top_dec_place(val_mantissa)
@@ -65,10 +64,10 @@ def get_val_unc_top_dec_place(
65
64
 
66
65
 
67
66
  def get_fixed_exp(
68
- input_exp: int | type(AutoExpVal),
67
+ input_exp: int | ExpValEnum,
69
68
  ) -> Literal[0]:
70
69
  """Get the exponent for fixed or percent format modes."""
71
- if input_exp is not AutoExpVal and input_exp != 0:
70
+ if input_exp is not ExpValEnum.AUTO and input_exp != 0:
72
71
  msg = "Cannot set non-zero exponent in fixed point or percent exponent mode."
73
72
  raise ValueError(msg)
74
73
  return 0
@@ -76,20 +75,20 @@ def get_fixed_exp(
76
75
 
77
76
  def get_scientific_exp(
78
77
  num: Decimal,
79
- input_exp: int | type(AutoExpVal),
78
+ input_exp: int | ExpValEnum,
80
79
  ) -> int:
81
80
  """Get the exponent for scientific formatting mode."""
82
- return get_top_dec_place(num) if input_exp is AutoExpVal else input_exp
81
+ return get_top_dec_place(num) if input_exp is ExpValEnum.AUTO else input_exp
83
82
 
84
83
 
85
84
  def get_engineering_exp(
86
85
  num: Decimal,
87
- input_exp: int | type(AutoExpVal),
86
+ input_exp: int | ExpValEnum,
88
87
  *,
89
88
  shifted: bool = False,
90
89
  ) -> int:
91
90
  """Get the exponent for engineering formatting modes."""
92
- if input_exp is AutoExpVal:
91
+ if input_exp is ExpValEnum.AUTO:
93
92
  exp_val = get_top_dec_place(num)
94
93
  exp_val = exp_val // 3 * 3 if not shifted else (exp_val + 1) // 3 * 3
95
94
  else:
@@ -105,12 +104,12 @@ def get_engineering_exp(
105
104
 
106
105
  def get_binary_exp(
107
106
  num: Decimal,
108
- input_exp: int | type(AutoExpVal),
107
+ input_exp: int | ExpValEnum,
109
108
  *,
110
109
  iec: bool = False,
111
110
  ) -> int:
112
111
  """Get the exponent for binary formatting modes."""
113
- if input_exp is AutoExpVal:
112
+ if input_exp is ExpValEnum.AUTO:
114
113
  exp_val = get_top_dec_place_binary(num)
115
114
  if iec:
116
115
  exp_val = (exp_val // 10) * 10
@@ -128,7 +127,7 @@ def get_binary_exp(
128
127
  def get_mantissa_exp_base(
129
128
  num: Decimal,
130
129
  exp_mode: ExpModeEnum,
131
- input_exp: int | type(AutoExpVal),
130
+ input_exp: int | ExpValEnum,
132
131
  ) -> tuple[Decimal, int, int]:
133
132
  """Get mantissa, exponent, and base for formatting a decimal number."""
134
133
  if exp_mode is ExpModeEnum.BINARY or exp_mode is ExpModeEnum.BINARY_IEC:
@@ -138,7 +137,7 @@ def get_mantissa_exp_base(
138
137
 
139
138
  if num == 0 or not num.is_finite():
140
139
  mantissa = Decimal(num)
141
- exp = 0 if input_exp is AutoExpVal else input_exp
140
+ exp = 0 if input_exp is ExpValEnum.AUTO else input_exp
142
141
  else:
143
142
  if exp_mode is ExpModeEnum.FIXEDPOINT or exp_mode is ExpModeEnum.PERCENT:
144
143
  exp = get_fixed_exp(input_exp)
@@ -8,10 +8,7 @@ from sciform.format_utils.numbers import (
8
8
  get_bottom_dec_place,
9
9
  get_top_dec_place,
10
10
  )
11
- from sciform.options.option_types import (
12
- AutoDigits,
13
- RoundModeEnum,
14
- )
11
+ from sciform.options.option_types import RoundModeEnum
15
12
 
16
13
 
17
14
  def get_pdg_round_digit(num: Decimal) -> int:
@@ -28,11 +25,13 @@ def get_pdg_round_digit(num: Decimal) -> int:
28
25
  if not num.is_finite():
29
26
  msg = f"num must be finite, not {num}."
30
27
  raise ValueError(msg)
28
+ if num == 0:
29
+ return 0
31
30
 
32
31
  top_dec_place = get_top_dec_place(num)
33
32
 
34
33
  # Bring num to be between 100 and 1000.
35
- num_top_three_digs = num * Decimal(10) ** (Decimal(2) - Decimal(top_dec_place))
34
+ num_top_three_digs = abs(num) * Decimal(10) ** (Decimal(2) - Decimal(top_dec_place))
36
35
  num_top_three_digs = num_top_three_digs.quantize(1, rounding="ROUND_FLOOR")
37
36
  new_top_dec_place = get_top_dec_place(num_top_three_digs)
38
37
  num_top_three_digs = num_top_three_digs * 10 ** (2 - new_top_dec_place)
@@ -59,21 +58,21 @@ def get_pdg_round_digit(num: Decimal) -> int:
59
58
  def get_round_dec_place(
60
59
  num: Decimal,
61
60
  round_mode: RoundModeEnum,
62
- ndigits: int | type(AutoDigits),
63
- *,
64
- pdg_sig_figs: bool = False,
61
+ ndigits: int,
65
62
  ) -> int:
66
63
  """Get the decimal place to which to round."""
67
64
  # TODO: Handle nan and inf
68
- if round_mode is RoundModeEnum.SIG_FIG:
69
- if pdg_sig_figs:
70
- round_digit = get_pdg_round_digit(num)
71
- elif ndigits is AutoDigits:
72
- round_digit = get_bottom_dec_place(num)
65
+ if round_mode is RoundModeEnum.ALL:
66
+ round_digit = get_bottom_dec_place(num)
67
+ elif round_mode is RoundModeEnum.PDG:
68
+ round_digit = get_pdg_round_digit(num)
69
+ elif round_mode is RoundModeEnum.SIG_FIG:
70
+ if num == 0:
71
+ round_digit = 0
73
72
  else:
74
73
  round_digit = get_top_dec_place(num) - (ndigits - 1)
75
74
  elif round_mode is RoundModeEnum.DEC_PLACE:
76
- round_digit = get_bottom_dec_place(num) if ndigits is AutoDigits else -ndigits
75
+ round_digit = -ndigits
77
76
  else:
78
77
  msg = f"Unhandled round mode: {round_mode}."
79
78
  raise ValueError(msg)
@@ -83,17 +82,15 @@ def get_round_dec_place(
83
82
  def round_val_unc(
84
83
  val: Decimal,
85
84
  unc: Decimal,
86
- ndigits: int | type[AutoDigits],
87
- *,
88
- use_pdg_sig_figs: bool = False,
85
+ round_mode: RoundModeEnum,
86
+ ndigits: int,
89
87
  ) -> tuple[Decimal, Decimal, int]:
90
88
  """Simultaneously round the value and uncertainty."""
91
89
  if unc.is_finite() and unc != 0:
92
90
  round_digit = get_round_dec_place(
93
91
  unc,
94
- RoundModeEnum.SIG_FIG,
92
+ round_mode,
95
93
  ndigits,
96
- pdg_sig_figs=use_pdg_sig_figs,
97
94
  )
98
95
  unc_rounded = round(unc, -round_digit)
99
96
  if val.is_finite():
@@ -103,9 +100,8 @@ def round_val_unc(
103
100
  elif val.is_finite():
104
101
  round_digit = get_round_dec_place(
105
102
  val,
106
- RoundModeEnum.SIG_FIG,
103
+ round_mode,
107
104
  ndigits,
108
- pdg_sig_figs=False,
109
105
  )
110
106
  unc_rounded = unc
111
107
  val_rounded = round(val, -round_digit)
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import re
6
6
 
7
7
  from sciform.options.input_options import InputOptions
8
+ from sciform.options.option_types import RoundModeEnum
8
9
 
9
10
  pattern = re.compile(
10
11
  r"""^
@@ -12,7 +13,11 @@ pattern = re.compile(
12
13
  (?P<sign_mode>[-+ ])?
13
14
  (?P<alternate_mode>\#)?
14
15
  (?P<left_pad_dec_place>\d+)?
15
- (?:(?P<round_mode>[.!])(?P<ndigits>[+-]?\d+))?
16
+ (?:
17
+ (?P<round_mode>[.!])(?P<ndigits>([+-]?\d+))
18
+ |
19
+ (?P<round_mode_special>[AP])
20
+ )?
16
21
  (?P<exp_mode>[fF%eErRbB])?
17
22
  (?:x(?P<exp_val>[+-]?\d+))?
18
23
  (?P<prefix_mode>p)?
@@ -51,6 +56,15 @@ def parse_exp_mode(
51
56
  return exp_mode, capitalize
52
57
 
53
58
 
59
+ ROUND_MODE_MAPPING = {
60
+ "!": "sig_fig",
61
+ ".": "dec_place",
62
+ "A": RoundModeEnum.ALL,
63
+ "P": RoundModeEnum.PDG,
64
+ None: None,
65
+ }
66
+
67
+
54
68
  def format_options_from_fmt_spec(fmt_spec: str) -> InputOptions:
55
69
  """Resolve InputOptions from format specification string."""
56
70
  match = pattern.match(fmt_spec)
@@ -68,16 +82,17 @@ def format_options_from_fmt_spec(fmt_spec: str) -> InputOptions:
68
82
  if left_pad_dec_place is not None:
69
83
  left_pad_dec_place = int(left_pad_dec_place)
70
84
 
71
- round_mode_mapping = {"!": "sig_fig", ".": "dec_place", None: None}
72
-
73
85
  round_mode_flag = match.group("round_mode")
74
- round_mode = round_mode_mapping[round_mode_flag]
75
-
76
86
  ndigits = match.group("ndigits")
77
- if ndigits is not None:
78
- ndigits = int(ndigits)
87
+ if not round_mode_flag:
88
+ special_round_mode_flag = match.group("round_mode_special")
89
+ if not special_round_mode_flag:
90
+ round_mode = None
91
+ else:
92
+ round_mode = ROUND_MODE_MAPPING[special_round_mode_flag]
79
93
  else:
80
- ndigits = None
94
+ round_mode = ROUND_MODE_MAPPING[round_mode_flag]
95
+ ndigits = int(ndigits)
81
96
 
82
97
  exp_mode = match.group("exp_mode")
83
98
  exp_mode, capitalize = parse_exp_mode(