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/__init__.py CHANGED
@@ -1,22 +1,30 @@
1
1
  """``sciform`` is used to convert python numbers into scientific formatted strings."""
2
2
 
3
3
  from sciform.formatter import Formatter
4
+ from sciform.formatting import FormattedNumber
4
5
  from sciform.global_configuration import (
5
- GlobalDefaultsContext,
6
- print_global_defaults,
7
- reset_global_defaults,
8
- set_global_defaults,
6
+ GlobalOptionsContext,
7
+ get_default_global_options,
8
+ get_global_options,
9
+ reset_global_options,
10
+ set_global_options,
9
11
  )
10
12
  from sciform.modes import AutoDigits, AutoExpVal
13
+ from sciform.options.input_options import InputOptions
14
+ from sciform.options.populated_options import PopulatedOptions
11
15
  from sciform.scinum import SciNum
12
16
 
13
17
  __all__ = [
14
18
  "Formatter",
15
- "GlobalDefaultsContext",
16
- "print_global_defaults",
17
- "reset_global_defaults",
18
- "set_global_defaults",
19
+ "FormattedNumber",
20
+ "GlobalOptionsContext",
21
+ "get_default_global_options",
22
+ "get_global_options",
23
+ "reset_global_options",
24
+ "set_global_options",
19
25
  "AutoDigits",
20
26
  "AutoExpVal",
21
27
  "SciNum",
28
+ "InputOptions",
29
+ "PopulatedOptions",
22
30
  ]
sciform/format_utils.py CHANGED
@@ -10,7 +10,6 @@ from typing import Literal, Union, cast
10
10
  from sciform.modes import (
11
11
  AutoDigits,
12
12
  AutoExpVal,
13
- ExpDriver,
14
13
  ExpFormatEnum,
15
14
  ExpModeEnum,
16
15
  RoundModeEnum,
@@ -155,7 +154,7 @@ def get_standard_exp_str(base: int, exp_val: int, *, capitalize: bool = False) -
155
154
 
156
155
 
157
156
  def get_superscript_exp_str(base: int, exp_val: int) -> str:
158
- """Get superscript (e.g. '×10⁺²') exponent string.""" # noqa: RUF002
157
+ """Get superscript (e.g. '×10⁺²') exponent string."""
159
158
  sup_trans = str.maketrans("+-0123456789", "⁺⁻⁰¹²³⁴⁵⁶⁷⁸⁹")
160
159
  exp_val_str = f"{exp_val}".translate(sup_trans)
161
160
  return f"×{base}{exp_val_str}"
@@ -198,8 +197,6 @@ def get_exp_str( # noqa: PLR0913
198
197
  extra_iec_prefixes: dict[int, str],
199
198
  extra_parts_per_forms: dict[int, str],
200
199
  capitalize: bool,
201
- latex: bool,
202
- latex_trim_whitespace: bool,
203
200
  superscript: bool,
204
201
  ) -> str:
205
202
  """Get formatting exponent string."""
@@ -225,43 +222,14 @@ def get_exp_str( # noqa: PLR0913
225
222
  if exp_val in text_exp_dict and text_exp_dict[exp_val] is not None:
226
223
  exp_str = f" {text_exp_dict[exp_val]}"
227
224
  exp_str = exp_str.rstrip(" ")
228
- if latex:
229
- if latex_trim_whitespace:
230
- exp_str = exp_str.lstrip(" ")
231
- exp_str = rf"\text{{{exp_str}}}"
232
225
  return exp_str
233
226
 
234
- if latex:
235
- return rf"\times {base}^{{{exp_val:+}}}"
236
227
  if superscript:
237
228
  return get_superscript_exp_str(base, exp_val)
238
229
 
239
230
  return get_standard_exp_str(base, exp_val, capitalize=capitalize)
240
231
 
241
232
 
242
- def parse_standard_exp_str(exp_str: str) -> tuple[int, int]:
243
- """Extract base and exponent value from standard exponent string."""
244
- match = re.match(
245
- r"""
246
- ^
247
- (?P<exp_symbol>[eEbB])
248
- (?P<exp_val>[+-]\d+)
249
- $
250
- """,
251
- exp_str,
252
- re.VERBOSE,
253
- )
254
-
255
- exp_symbol = match.group("exp_symbol")
256
- symbol_to_base_dict = {"e": 10, "b": 2}
257
- base = symbol_to_base_dict[exp_symbol.lower()]
258
-
259
- exp_val_str = match.group("exp_val")
260
- exp_val = int(exp_val_str)
261
-
262
- return base, exp_val
263
-
264
-
265
233
  def get_sign_str(num: Decimal, sign_mode: SignModeEnum) -> str:
266
234
  """Get the format sign string."""
267
235
  if num < 0:
@@ -338,11 +306,11 @@ def get_round_digit(
338
306
  return round_digit
339
307
 
340
308
 
341
- def get_fill_str(fill_char: str, top_digit: int, top_padded_digit: int) -> str:
342
- """Get the string filling from top_digit place to top_padded_digit place."""
309
+ def get_pad_str(left_pad_char: str, top_digit: int, top_padded_digit: int) -> str:
310
+ """Get the string padding from top_digit place to top_padded_digit place."""
343
311
  if top_padded_digit > top_digit:
344
312
  pad_len = top_padded_digit - max(top_digit, 0)
345
- pad_str = fill_char * pad_len
313
+ pad_str = left_pad_char * pad_len
346
314
  else:
347
315
  pad_str = ""
348
316
  return pad_str
@@ -353,7 +321,7 @@ def format_num_by_top_bottom_dig(
353
321
  target_top_digit: int,
354
322
  target_bottom_digit: int,
355
323
  sign_mode: SignModeEnum,
356
- fill_char: str,
324
+ left_pad_char: str,
357
325
  ) -> str:
358
326
  """Format a number according to specified top and bottom digit places."""
359
327
  print_prec = max(0, -target_bottom_digit)
@@ -362,26 +330,8 @@ def format_num_by_top_bottom_dig(
362
330
  sign_str = get_sign_str(num, sign_mode)
363
331
 
364
332
  num_top_digit = get_top_digit(num)
365
- fill_str = get_fill_str(fill_char, num_top_digit, target_top_digit)
366
- return f"{sign_str}{fill_str}{abs_mantissa_str}"
367
-
368
-
369
- def latex_translate(input_str: str) -> str:
370
- """Translate elements of a string for Latex compatibility."""
371
- result_str = input_str
372
- replacements = (
373
- ("(", r"\left("),
374
- (")", r"\right)"),
375
- ("%", r"\%"),
376
- ("_", r"\_"),
377
- ("nan", r"\text{nan}"),
378
- ("NAN", r"\text{NAN}"),
379
- ("inf", r"\text{inf}"),
380
- ("INF", r"\text{INF}"),
381
- )
382
- for old_chars, new_chars in replacements:
383
- result_str = result_str.replace(old_chars, new_chars)
384
- return result_str
333
+ pad_str = get_pad_str(left_pad_char, num_top_digit, target_top_digit)
334
+ return f"{sign_str}{pad_str}{abs_mantissa_str}"
385
335
 
386
336
 
387
337
  def round_val_unc(
@@ -420,24 +370,17 @@ def get_val_unc_exp(
420
370
  unc: Decimal,
421
371
  exp_mode: ExpModeEnum,
422
372
  input_exp: int,
423
- ) -> tuple[int, ExpDriver]:
373
+ ) -> int:
424
374
  """Get exponent for value/uncertainty formatting."""
425
375
  if val.is_finite() and unc.is_finite():
426
376
  if abs(val) >= unc:
427
- exp_driver_type = ExpDriver.VAL
377
+ exp_driver_val = val
428
378
  else:
429
- exp_driver_type = ExpDriver.UNC
379
+ exp_driver_val = unc
430
380
  elif val.is_finite():
431
- exp_driver_type = ExpDriver.VAL
432
- else:
433
- exp_driver_type = ExpDriver.UNC
434
-
435
- if exp_driver_type is ExpDriver.VAL:
436
381
  exp_driver_val = val
437
- elif exp_driver_type is ExpDriver.UNC:
382
+ else:
438
383
  exp_driver_val = unc
439
- else: # pragma: no cover
440
- raise ValueError
441
384
 
442
385
  _, exp_val, _ = get_mantissa_exp_base(
443
386
  exp_driver_val,
@@ -445,7 +388,7 @@ def get_val_unc_exp(
445
388
  input_exp=input_exp,
446
389
  )
447
390
 
448
- return exp_val, exp_driver_type
391
+ return exp_val
449
392
 
450
393
 
451
394
  def get_val_unc_top_digit(
@@ -469,11 +412,10 @@ def get_val_unc_top_digit(
469
412
  return new_top_digit
470
413
 
471
414
 
472
- def get_val_unc_mantissa_exp_strs(
415
+ def get_val_unc_mantissa_strs(
473
416
  val_mantissa_exp_str: str,
474
417
  unc_mantissa_exp_str: str,
475
- exp_driver_type: ExpDriver,
476
- ) -> tuple[str, str, str]:
418
+ ) -> tuple[str, str]:
477
419
  """Break val/unc mantissa/exp strings into mantissa strings and an exp string."""
478
420
  # Optional parentheses needed to handle (nan)e+00 case
479
421
  mantissa_exp_pattern = re.compile(
@@ -485,15 +427,7 @@ def get_val_unc_mantissa_exp_strs(
485
427
  unc_match = mantissa_exp_pattern.match(unc_mantissa_exp_str)
486
428
  unc_mantissa_str = unc_match.group("mantissa_str")
487
429
 
488
- if exp_driver_type is ExpDriver.VAL:
489
- exp_str = val_match.group("exp_str")
490
- elif exp_driver_type is ExpDriver.UNC:
491
- exp_str = unc_match.group("exp_str")
492
- else:
493
- msg = f"Invalid exp_driver_type: {exp_driver_type}."
494
- raise ValueError(msg)
495
-
496
- return val_mantissa_str, unc_mantissa_str, exp_str
430
+ return val_mantissa_str, unc_mantissa_str
497
431
 
498
432
 
499
433
  def construct_val_unc_str( # noqa: PLR0913
@@ -504,13 +438,12 @@ def construct_val_unc_str( # noqa: PLR0913
504
438
  decimal_separator: SeparatorEnum,
505
439
  *,
506
440
  paren_uncertainty: bool,
507
- latex: bool,
508
441
  pm_whitespace: bool,
509
442
  paren_uncertainty_separators: bool,
510
443
  ) -> str:
511
444
  """Construct the value/uncertainty part of the formatted string."""
512
445
  if not paren_uncertainty:
513
- pm_symb = r"\pm" if latex else "±"
446
+ pm_symb = "±"
514
447
  if pm_whitespace:
515
448
  pm_symb = f" {pm_symb} "
516
449
  val_unc_str = f"{val_mantissa_str}{pm_symb}{unc_mantissa_str}"
@@ -555,7 +488,6 @@ def construct_val_unc_exp_str( # noqa: PLR0913
555
488
  extra_iec_prefixes: dict[int, str | None],
556
489
  extra_parts_per_forms: dict[int, str | None],
557
490
  capitalize: bool,
558
- latex: bool,
559
491
  superscript: bool,
560
492
  paren_uncertainty: bool,
561
493
  ) -> str:
@@ -565,22 +497,22 @@ def construct_val_unc_exp_str( # noqa: PLR0913
565
497
  exp_mode=exp_mode,
566
498
  exp_format=exp_format,
567
499
  capitalize=capitalize,
568
- latex=latex,
569
- latex_trim_whitespace=True,
570
500
  superscript=superscript,
571
501
  extra_si_prefixes=extra_si_prefixes,
572
502
  extra_iec_prefixes=extra_iec_prefixes,
573
503
  extra_parts_per_forms=extra_parts_per_forms,
574
504
  )
575
505
 
576
- """
577
- "1234(12)" val_unc_str along with exp_str "k" will be formatted as
578
- "1234(12) k whereas 1234(12) with exp_str "e+03" will be
579
- formatted as (1234(12))e+03.
580
- "1234 ± 12" will be formatted with parentheses as "(1234 ± 12) k" or
581
- "(1234 ± 12)e+03"
582
- """
583
- if paren_uncertainty and not re.match(r"^[eEbB][+-]\d+$", exp_str):
506
+ if exp_str == "":
507
+ val_unc_exp_str = f"{val_unc_str}{exp_str}"
508
+ elif paren_uncertainty and not re.match(r"^[eEbB][+-]\d+$", exp_str):
509
+ """
510
+ "1234(12)" val_unc_str along with exp_str "k" will be formatted as
511
+ "1234(12) k whereas 1234(12) with exp_str "e+03" will be
512
+ formatted as (1234(12))e+03.
513
+ "1234 ± 12" will be formatted with parentheses as "(1234 ± 12) k" or
514
+ "(1234 ± 12)e+03"
515
+ """
584
516
  val_unc_exp_str = f"{val_unc_str}{exp_str}"
585
517
  else:
586
518
  val_unc_exp_str = f"({val_unc_str}){exp_str}"
sciform/formatter.py CHANGED
@@ -2,41 +2,132 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from decimal import Decimal
6
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Literal
7
6
 
8
- from sciform.formatting import format_num, format_val_unc
9
- from sciform.user_options import UserOptions
7
+ from sciform.formatting import format_from_options
8
+ from sciform.options.conversion import populate_options
9
+ from sciform.options.input_options import InputOptions
10
10
 
11
11
  if TYPE_CHECKING: # pragma: no cover
12
12
  from sciform import modes
13
13
  from sciform.format_utils import Number
14
+ from sciform.formatting import FormattedNumber
15
+ from sciform.options.populated_options import PopulatedOptions
14
16
 
15
17
 
16
18
  class Formatter:
17
- """
18
- Class to format numbers and pairs of numbers into strings.
19
+ r"""
20
+ Class to format value and value/uncertainty pairs.
19
21
 
20
- :class:`Formatter` is used to convert numbers and pairs of numbers
21
- into formatted strings according to a variety of formatting options.
22
- See :ref:`formatting_options` for more details on the available
23
- options. Any options which are unpopulated (have the value ``None``)
24
- will be populated at format time by the corresponding values in the
25
- globally configured default options. See :ref:`global_config` for
26
- details about how to view and modify the global default options.
22
+ :class:`Formatter` is used to convert value and value/uncertainty
23
+ pairs into formatted strings according to a variety of formatting
24
+ options. See :ref:`formatting_options` for more details on the
25
+ available options. Any options which are not populated (not passed
26
+ in or passed in the ``None`` value) will be populated at format time
27
+ by the corresponding values in the globally configured default
28
+ options. See :ref:`global_config` for details about how to view and
29
+ modify the global options. The user supplied options cannot be
30
+ updated after the :class:`Formatter` is constructed.
31
+
32
+ After initialization, the :class:`Formatter` is used by passing in
33
+ a value into the :class:`Formatter`.
27
34
 
28
35
  >>> from sciform import Formatter
29
- >>> sform = Formatter(exp_mode="engineering", round_mode="sig_fig", ndigits=4)
30
- >>> print(sform(12345.678))
36
+ >>> formatter = Formatter(exp_mode="engineering", round_mode="sig_fig", ndigits=4)
37
+ >>> print(formatter(12345.678))
31
38
  12.35e+03
32
39
 
33
- The Formatter can be called with two aguments for value/uncertainty
34
- formatting
40
+ A value/uncertainty pair can also be passed into the
41
+ :class:`Formatter`.
42
+
43
+ >>> formatter = Formatter(
44
+ ... exp_mode="engineering",
45
+ ... round_mode="sig_fig",
46
+ ... ndigits=2,
47
+ ... superscript=True,
48
+ ... )
49
+ >>> formatted = formatter(12345.678, 3.4)
50
+ >>> print(formatted)
51
+ (12.3457 ± 0.0034)×10³
52
+
53
+ The returned object behaves like a ``str``, but is, in fact, a
54
+ :class:`FormattedNumber` instance. The :class:`FormattedNumber` is
55
+ a subclass of ``str`` but provides methods for post-conversion into
56
+ LaTeX, HTML, and ASCII formats.
57
+
58
+ >>> print(formatted.as_latex())
59
+ $(12.3457\:\pm\:0.0034)\times10^{3}$
60
+ >>> print(formatted.as_html())
61
+ (12.3457 ± 0.0034)×10<sup>3</sup>
62
+ >>> print(formatted.as_ascii())
63
+ (12.3457 +/- 0.0034)e+03
64
+
65
+ The formatting options input by the user can be checked by
66
+ inspecting the :attr:`input_options` property
67
+
68
+ >>> print(formatter.input_options)
69
+ InputOptions(
70
+ 'exp_mode': 'engineering',
71
+ 'round_mode': 'sig_fig',
72
+ 'ndigits': 2,
73
+ 'superscript': True,
74
+ )
75
+
76
+ Only explicitly populated options appear in the string printout.
77
+ However, populated and unpopulated parameters can be inspected by
78
+ direct attribute access. Unpopulated parameters are ``None``-valued.
79
+
80
+ >>> print(formatter.input_options.round_mode)
81
+ sig_fig
82
+ >>> print(formatter.input_options.exp_format)
83
+ None
84
+
85
+ The :meth:`InputOptions.as_dict` method returns a dictionary of
86
+ input options that can be passed back into a :class:`Formatter`
87
+ constructor as ``**kwargs``, possibly after modification. Only
88
+ explicitly populated options are included in this dictionary.
89
+
90
+ >>> print(formatter.input_options.as_dict())
91
+ {'exp_mode': 'engineering', 'round_mode': 'sig_fig', 'ndigits': 2, 'superscript': True}
92
+
93
+ Likewise, the result of populating the options with the global
94
+ options can be previewed by inspecting the :attr:`populated_options`
95
+ property.
35
96
 
36
- >>> sform = Formatter(exp_mode="engineering", round_mode="sig_fig", ndigits=2)
37
- >>> print(sform(12345.678, 3.4))
38
- (12.3457 ± 0.0034)e+03
39
- """
97
+ >>> print(formatter.populated_options)
98
+ PopulatedOptions(
99
+ 'exp_mode': 'engineering',
100
+ 'exp_val': AutoExpVal,
101
+ 'round_mode': 'sig_fig',
102
+ 'ndigits': 2,
103
+ 'upper_separator': '',
104
+ 'decimal_separator': '.',
105
+ 'lower_separator': '',
106
+ 'sign_mode': '-',
107
+ 'left_pad_char': ' ',
108
+ 'left_pad_dec_place': 0,
109
+ 'exp_format': 'standard',
110
+ 'extra_si_prefixes': {},
111
+ 'extra_iec_prefixes': {},
112
+ 'extra_parts_per_forms': {},
113
+ 'capitalize': False,
114
+ 'superscript': True,
115
+ 'nan_inf_exp': False,
116
+ 'paren_uncertainty': False,
117
+ 'pdg_sig_figs': False,
118
+ 'left_pad_matching': False,
119
+ 'paren_uncertainty_separators': True,
120
+ 'pm_whitespace': True,
121
+ )
122
+ >>> print(formatter.populated_options.exp_format)
123
+ standard
124
+
125
+ The :class:`PopulatedOptions` class also provides a
126
+ :class:`PopulatedOptions.as_dict` method which can be used to
127
+ construct ``**kwargs`` to pass into new :class:`Formatter`
128
+ instances.
129
+
130
+ """ # noqa: E501
40
131
 
41
132
  def __init__( # noqa: PLR0913
42
133
  self: Formatter,
@@ -49,7 +140,7 @@ class Formatter:
49
140
  decimal_separator: modes.DecimalSeparators | None = None,
50
141
  lower_separator: modes.LowerSeparators | None = None,
51
142
  sign_mode: modes.SignMode | None = None,
52
- fill_char: modes.FillChar | None = None,
143
+ left_pad_char: modes.LeftPadChar | Literal[0] | None = None,
53
144
  left_pad_dec_place: int | None = None,
54
145
  exp_format: modes.ExpFormat | None = None,
55
146
  extra_si_prefixes: dict[int, str] | None = None,
@@ -57,18 +148,17 @@ class Formatter:
57
148
  extra_parts_per_forms: dict[int, str] | None = None,
58
149
  capitalize: bool | None = None,
59
150
  superscript: bool | None = None,
60
- latex: bool | None = None,
61
151
  nan_inf_exp: bool | None = None,
62
152
  paren_uncertainty: bool | None = None,
63
153
  pdg_sig_figs: bool | None = None,
64
154
  left_pad_matching: bool | None = None,
65
155
  paren_uncertainty_separators: bool | None = None,
66
156
  pm_whitespace: bool | None = None,
67
- add_c_prefix: bool = False,
68
- add_small_si_prefixes: bool = False,
69
- add_ppth_form: bool = False,
157
+ add_c_prefix: bool | None = None,
158
+ add_small_si_prefixes: bool | None = None,
159
+ add_ppth_form: bool | None = None,
70
160
  ) -> None:
71
- r"""
161
+ """
72
162
  Create a new ``Formatter``.
73
163
 
74
164
  The following checks are performed when creating a new
@@ -119,8 +209,9 @@ class Formatter:
119
209
  :type lower_separator: ``Literal['', ' ', '_'] | None``
120
210
  :param sign_mode: Indicate sign symbol behavior.
121
211
  :type sign_mode: ``Literal['-', '+', ' '] | None``
122
- :param fill_char: Indicate whether to fill with zeros or spaces.
123
- :type fill_char: ``Literal[' ', '0'] | None``
212
+ :param left_pad_char: Indicate whether to pad with zeros or
213
+ spaces.
214
+ :type left_pad_char: ``Literal[' ', '0', 0] | None``
124
215
  :param left_pad_dec_place: Positive ``int`` indicating the
125
216
  decimal place to which the string will be left padded before
126
217
  the sign symbol. 0 corresponds to the ones place, 1
@@ -151,10 +242,6 @@ class Formatter:
151
242
  should be converted into superscript notation. E.g.
152
243
  ``'1.23e+02'`` is converted to ``'1.23×10²'``
153
244
  :type superscript: ``bool | None``
154
- :param latex: Flag indicating if the resulting string should be
155
- converted into a latex parseable code, e.g.
156
- ``'\\left(1.23 \\pm 0.01\\right)\\times 10^{2}'``.
157
- :type latex: ``bool | None``
158
245
  :param nan_inf_exp: Flag indicating whether non-finite numbers
159
246
  such as ``float('nan')`` or ``float('inf')`` should be
160
247
  formatted with exponent symbols when exponent modes including
@@ -182,18 +269,19 @@ class Formatter:
182
269
  whitespace surrounding the ``'±'`` symbols when formatting.
183
270
  E.g. ``123.4±2.3`` compared to ``123.4 ± 2.3``.
184
271
  :type pm_whitespace: ``bool | None``
185
- :param add_c_prefix: (default ``False``) If ``True``, adds
186
- ``{-2: 'c'}`` to ``extra_si_prefixes``.
187
- :type add_c_prefix: ``bool``
188
- :param add_small_si_prefixes: (default ``False``) If ``True``, adds
272
+ :param add_c_prefix: (default ``None`` is like ``False``) If
273
+ ``True``, adds ``{-2: 'c'}`` to ``extra_si_prefixes``.
274
+ :type add_c_prefix: ``bool | None``
275
+ :param add_small_si_prefixes: (default ``None`` is like
276
+ ``False``) If ``True``, adds
189
277
  ``{-2: 'c', -1: 'd', +1: 'da', +2: 'h'}`` to
190
278
  ``extra_si_prefixes``.
191
- :type add_small_si_prefixes: ``bool``
192
- :param add_ppth_form: (default ``False``) if ``True``, adds
193
- ``{-3: 'ppth'}`` to ``extra_parts_per_forms``.
194
- :type add_ppth_form: ``bool``
195
- """ # noqa: RUF002
196
- self._user_options = UserOptions(
279
+ :type add_small_si_prefixes: ``bool | None``
280
+ :param add_ppth_form: (default ``None`` is like ``False``) if
281
+ ``True``, adds ``{-3: 'ppth'}`` to ``extra_parts_per_forms``.
282
+ :type add_ppth_form: ``bool | None``
283
+ """
284
+ self._input_options = InputOptions(
197
285
  exp_mode=exp_mode,
198
286
  exp_val=exp_val,
199
287
  round_mode=round_mode,
@@ -202,7 +290,7 @@ class Formatter:
202
290
  decimal_separator=decimal_separator,
203
291
  lower_separator=lower_separator,
204
292
  sign_mode=sign_mode,
205
- fill_char=fill_char,
293
+ left_pad_char=left_pad_char,
206
294
  left_pad_dec_place=left_pad_dec_place,
207
295
  exp_format=exp_format,
208
296
  extra_si_prefixes=extra_si_prefixes,
@@ -210,7 +298,6 @@ class Formatter:
210
298
  extra_parts_per_forms=extra_parts_per_forms,
211
299
  capitalize=capitalize,
212
300
  superscript=superscript,
213
- latex=latex,
214
301
  nan_inf_exp=nan_inf_exp,
215
302
  paren_uncertainty=paren_uncertainty,
216
303
  pdg_sig_figs=pdg_sig_figs,
@@ -222,12 +309,28 @@ class Formatter:
222
309
  add_ppth_form=add_ppth_form,
223
310
  )
224
311
 
312
+ @property
313
+ def input_options(self: Formatter) -> InputOptions:
314
+ """Return user input options as :class:`InputOptions` instance."""
315
+ return self._input_options
316
+
317
+ @property
318
+ def populated_options(self: Formatter) -> PopulatedOptions:
319
+ """
320
+ Return fully populated options as :class:`PopulatedOptions` instance.
321
+
322
+ :attr:`populated_options` is re-calculated from
323
+ :attr:`input_options` and the global options each time it is
324
+ accessed so that it always reflects the current global options.
325
+ """
326
+ return populate_options(self.input_options)
327
+
225
328
  def __call__(
226
329
  self: Formatter,
227
330
  value: Number,
228
331
  uncertainty: Number | None = None,
229
332
  /,
230
- ) -> str:
333
+ ) -> FormattedNumber:
231
334
  """
232
335
  Format a value or value/uncertainty pair.
233
336
 
@@ -236,13 +339,8 @@ class Formatter:
236
339
  :param uncertainty: Optional uncertainty to be formatted.
237
340
  :type uncertainty: ``Decimal | float | int | str | None``
238
341
  """
239
- rendered_options = self._user_options.render()
240
- if uncertainty is None:
241
- output = format_num(Decimal(str(value)), rendered_options)
242
- else:
243
- output = format_val_unc(
244
- Decimal(str(value)),
245
- Decimal(str(uncertainty)),
246
- rendered_options,
247
- )
248
- return output
342
+ return format_from_options(
343
+ value,
344
+ uncertainty,
345
+ input_options=self.input_options,
346
+ )