sciform 0.33.0__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 +16 -8
- sciform/formatter.py +149 -90
- sciform/formatting.py +92 -5
- sciform/fsml.py +4 -4
- sciform/global_configuration.py +35 -28
- sciform/options/__init__.py +1 -0
- sciform/options/conversion.py +120 -0
- sciform/{rendered_options.py → options/finalized_options.py} +7 -11
- sciform/{global_options.py → options/global_options.py} +10 -10
- sciform/options/input_options.py +104 -0
- sciform/options/populated_options.py +136 -0
- sciform/options/validation.py +101 -0
- sciform/output_conversion.py +2 -2
- sciform/scinum.py +13 -12
- {sciform-0.33.0.dist-info → sciform-0.34.0.dist-info}/METADATA +13 -9
- sciform-0.34.0.dist-info/RECORD +23 -0
- sciform/user_options.py +0 -188
- sciform-0.33.0.dist-info/RECORD +0 -19
- {sciform-0.33.0.dist-info → sciform-0.34.0.dist-info}/LICENSE +0 -0
- {sciform-0.33.0.dist-info → sciform-0.34.0.dist-info}/WHEEL +0 -0
- {sciform-0.33.0.dist-info → sciform-0.34.0.dist-info}/top_level.txt +0 -0
@@ -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
|
@@ -1,23 +1,23 @@
|
|
1
1
|
"""
|
2
2
|
Rendered format options used in sciform backend formatting algorithm.
|
3
3
|
|
4
|
-
:class:`
|
4
|
+
:class:`InputOptions` are converted into :class:`FinalizedOptions`
|
5
5
|
internally at format time.
|
6
6
|
"""
|
7
7
|
|
8
8
|
from __future__ import annotations
|
9
9
|
|
10
|
-
from dataclasses import
|
11
|
-
from enum import Enum
|
12
|
-
from pprint import pformat
|
10
|
+
from dataclasses import dataclass
|
13
11
|
from typing import TYPE_CHECKING
|
14
12
|
|
13
|
+
from sciform.options.validation import validate_options
|
14
|
+
|
15
15
|
if TYPE_CHECKING: # pragma: no cover
|
16
16
|
from sciform import modes
|
17
17
|
|
18
18
|
|
19
19
|
@dataclass(frozen=True)
|
20
|
-
class
|
20
|
+
class FinalizedOptions:
|
21
21
|
"""Rendered options: All options populated and using Enum instead of Literal."""
|
22
22
|
|
23
23
|
exp_mode: modes.ExpModeEnum
|
@@ -43,9 +43,5 @@ class RenderedOptions:
|
|
43
43
|
paren_uncertainty_separators: bool
|
44
44
|
pm_whitespace: bool
|
45
45
|
|
46
|
-
def
|
47
|
-
|
48
|
-
for key, value in options_dict.items():
|
49
|
-
if isinstance(value, Enum):
|
50
|
-
options_dict[key] = value.value
|
51
|
-
return pformat(options_dict, sort_dicts=False)
|
46
|
+
def __post_init__(self: FinalizedOptions) -> None:
|
47
|
+
validate_options(self)
|
@@ -1,20 +1,20 @@
|
|
1
1
|
"""Global Options."""
|
2
2
|
|
3
3
|
from sciform import modes
|
4
|
-
from sciform.
|
4
|
+
from sciform.options.populated_options import PopulatedOptions
|
5
5
|
|
6
|
-
PKG_DEFAULT_OPTIONS =
|
7
|
-
exp_mode=
|
6
|
+
PKG_DEFAULT_OPTIONS = PopulatedOptions(
|
7
|
+
exp_mode="fixed_point",
|
8
8
|
exp_val=modes.AutoExpVal,
|
9
|
-
round_mode=
|
9
|
+
round_mode="sig_fig",
|
10
10
|
ndigits=modes.AutoDigits,
|
11
|
-
upper_separator=
|
12
|
-
decimal_separator=
|
13
|
-
lower_separator=
|
14
|
-
sign_mode=
|
15
|
-
left_pad_char=
|
11
|
+
upper_separator="",
|
12
|
+
decimal_separator=".",
|
13
|
+
lower_separator="",
|
14
|
+
sign_mode="-",
|
15
|
+
left_pad_char=" ",
|
16
16
|
left_pad_dec_place=0,
|
17
|
-
exp_format=
|
17
|
+
exp_format="standard",
|
18
18
|
extra_si_prefixes={},
|
19
19
|
extra_iec_prefixes={},
|
20
20
|
extra_parts_per_forms={},
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""InputOptions Dataclass which stores user input."""
|
2
|
+
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from dataclasses import asdict, dataclass
|
7
|
+
from pprint import pformat
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal
|
9
|
+
|
10
|
+
from sciform.options.validation import validate_options
|
11
|
+
|
12
|
+
if TYPE_CHECKING: # pragma: no cover
|
13
|
+
from sciform import modes
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass(frozen=True)
|
17
|
+
class InputOptions:
|
18
|
+
"""
|
19
|
+
Dataclass storing user input.
|
20
|
+
|
21
|
+
Stores the user input to :class:`Formatter`, so any keyword
|
22
|
+
arguments that can be passed into :class:`Formatter` are stored in
|
23
|
+
:class:`InputOptions`. Any unpopulated options retain the ``None``
|
24
|
+
value. At format time the :class:`InputOptions` are populated and
|
25
|
+
replaced by a :class:`PopulatedOptions` instance which necessarily
|
26
|
+
has all attributes populated with meaningful values.
|
27
|
+
|
28
|
+
:class:`InputOptions` instances should only be accessed via the
|
29
|
+
:class:`Formatter.input_options()` property. They should not be
|
30
|
+
instantiated directly.
|
31
|
+
|
32
|
+
>>> from sciform import Formatter
|
33
|
+
>>> formatter = Formatter(
|
34
|
+
... exp_mode="engineering",
|
35
|
+
... round_mode="sig_fig",
|
36
|
+
... ndigits=2,
|
37
|
+
... superscript=True,
|
38
|
+
... )
|
39
|
+
>>> print(formatter.input_options.round_mode)
|
40
|
+
sig_fig
|
41
|
+
>>> print(formatter.input_options.exp_format)
|
42
|
+
None
|
43
|
+
>>> print(formatter.input_options)
|
44
|
+
InputOptions(
|
45
|
+
'exp_mode': 'engineering',
|
46
|
+
'round_mode': 'sig_fig',
|
47
|
+
'ndigits': 2,
|
48
|
+
'superscript': True,
|
49
|
+
)
|
50
|
+
>>> print(formatter.input_options.as_dict())
|
51
|
+
{'exp_mode': 'engineering', 'round_mode': 'sig_fig', 'ndigits': 2, 'superscript': True}
|
52
|
+
""" # noqa: E501
|
53
|
+
|
54
|
+
exp_mode: modes.ExpMode | None = None
|
55
|
+
exp_val: int | type(modes.AutoExpVal) | None = None
|
56
|
+
round_mode: modes.RoundMode | None = None
|
57
|
+
ndigits: int | type(modes.AutoDigits) | None = None
|
58
|
+
upper_separator: modes.UpperSeparators | None = None
|
59
|
+
decimal_separator: modes.DecimalSeparators | None = None
|
60
|
+
lower_separator: modes.LowerSeparators | None = None
|
61
|
+
sign_mode: modes.SignMode | None = None
|
62
|
+
left_pad_char: modes.LeftPadChar | Literal[0] | None = None
|
63
|
+
left_pad_dec_place: int | None = None
|
64
|
+
exp_format: modes.ExpFormat | None = None
|
65
|
+
extra_si_prefixes: dict[int, str] | None = None
|
66
|
+
extra_iec_prefixes: dict[int, str] | None = None
|
67
|
+
extra_parts_per_forms: dict[int, str] | None = None
|
68
|
+
capitalize: bool | None = None
|
69
|
+
superscript: bool | None = None
|
70
|
+
nan_inf_exp: bool | None = None
|
71
|
+
paren_uncertainty: bool | None = None
|
72
|
+
pdg_sig_figs: bool | None = None
|
73
|
+
left_pad_matching: bool | None = None
|
74
|
+
paren_uncertainty_separators: bool | None = None
|
75
|
+
pm_whitespace: bool | None = None
|
76
|
+
|
77
|
+
add_c_prefix: bool = None
|
78
|
+
add_small_si_prefixes: bool = None
|
79
|
+
add_ppth_form: bool = None
|
80
|
+
|
81
|
+
def __post_init__(self: InputOptions) -> None:
|
82
|
+
validate_options(self)
|
83
|
+
|
84
|
+
def as_dict(self: InputOptions) -> dict[str, Any]:
|
85
|
+
"""
|
86
|
+
Return a dict representation of the InputOptions.
|
87
|
+
|
88
|
+
This dict can be passed into :class:`Formatter` as ``**kwargs``,
|
89
|
+
possibly after modification. This allows for the possibility of
|
90
|
+
constructing new :class:`Formatter` instances based on old ones.
|
91
|
+
Only explicitly populated attributes are included in the
|
92
|
+
returned dictionary.
|
93
|
+
"""
|
94
|
+
options_dict = asdict(self)
|
95
|
+
for key in list(options_dict.keys()):
|
96
|
+
if options_dict[key] is None:
|
97
|
+
del options_dict[key]
|
98
|
+
return options_dict
|
99
|
+
|
100
|
+
def __str__(self: InputOptions) -> str:
|
101
|
+
options_str = pformat(self.as_dict(), width=-1, sort_dicts=False)
|
102
|
+
options_str = options_str.lstrip("{").rstrip("}")
|
103
|
+
options_str = f"InputOptions(\n {options_str},\n)"
|
104
|
+
return options_str
|
@@ -0,0 +1,136 @@
|
|
1
|
+
"""InputOptions Dataclass which stores user input."""
|
2
|
+
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from dataclasses import asdict, dataclass
|
7
|
+
from pprint import pformat
|
8
|
+
from typing import TYPE_CHECKING, Any
|
9
|
+
|
10
|
+
from sciform.options.validation import validate_options
|
11
|
+
|
12
|
+
if TYPE_CHECKING: # pragma: no cover
|
13
|
+
from sciform import modes
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass(frozen=True)
|
17
|
+
class PopulatedOptions:
|
18
|
+
"""
|
19
|
+
Dataclass storing fully populated formatting options.
|
20
|
+
|
21
|
+
User input options during :class:`Formatter` initialization are
|
22
|
+
stored in :class:`InputOptions` instances. But
|
23
|
+
:class:`InputOptions` instances don't necessarily have all options
|
24
|
+
populated as required for the formatting algorithm. At formatting
|
25
|
+
time the unpopulated options are populated from the global options.
|
26
|
+
The new resulting options object with all options populated is a
|
27
|
+
:class:`PopulatedOptions` instances. Note that the global options
|
28
|
+
are stored as a :class:`PopulatedOptions` instance.
|
29
|
+
|
30
|
+
:class:`PopulatedOptions` instances should only be accessed via the
|
31
|
+
:class:`Formatter.populated_options()` property. They should not be
|
32
|
+
instantiated directly.
|
33
|
+
|
34
|
+
>>> from sciform import Formatter
|
35
|
+
>>> formatter = Formatter(
|
36
|
+
... exp_mode="engineering",
|
37
|
+
... round_mode="sig_fig",
|
38
|
+
... ndigits=2,
|
39
|
+
... superscript=True,
|
40
|
+
... )
|
41
|
+
>>> print(formatter.populated_options.round_mode)
|
42
|
+
sig_fig
|
43
|
+
>>> print(formatter.populated_options.exp_format)
|
44
|
+
standard
|
45
|
+
>>> print(formatter.populated_options)
|
46
|
+
PopulatedOptions(
|
47
|
+
'exp_mode': 'engineering',
|
48
|
+
'exp_val': AutoExpVal,
|
49
|
+
'round_mode': 'sig_fig',
|
50
|
+
'ndigits': 2,
|
51
|
+
'upper_separator': '',
|
52
|
+
'decimal_separator': '.',
|
53
|
+
'lower_separator': '',
|
54
|
+
'sign_mode': '-',
|
55
|
+
'left_pad_char': ' ',
|
56
|
+
'left_pad_dec_place': 0,
|
57
|
+
'exp_format': 'standard',
|
58
|
+
'extra_si_prefixes': {},
|
59
|
+
'extra_iec_prefixes': {},
|
60
|
+
'extra_parts_per_forms': {},
|
61
|
+
'capitalize': False,
|
62
|
+
'superscript': True,
|
63
|
+
'nan_inf_exp': False,
|
64
|
+
'paren_uncertainty': False,
|
65
|
+
'pdg_sig_figs': False,
|
66
|
+
'left_pad_matching': False,
|
67
|
+
'paren_uncertainty_separators': True,
|
68
|
+
'pm_whitespace': True,
|
69
|
+
)
|
70
|
+
>>> print(formatter.populated_options.as_dict())
|
71
|
+
{'exp_mode': 'engineering', 'exp_val': AutoExpVal, 'round_mode': 'sig_fig', 'ndigits': 2, 'upper_separator': '', 'decimal_separator': '.', 'lower_separator': '', 'sign_mode': '-', 'left_pad_char': ' ', 'left_pad_dec_place': 0, 'exp_format': 'standard', 'extra_si_prefixes': {}, 'extra_iec_prefixes': {}, 'extra_parts_per_forms': {}, 'capitalize': False, 'superscript': True, 'nan_inf_exp': False, 'paren_uncertainty': False, 'pdg_sig_figs': False, 'left_pad_matching': False, 'paren_uncertainty_separators': True, 'pm_whitespace': True}
|
72
|
+
|
73
|
+
Note that :class:`PopulatedOptions` lacks the ``add_c_prefix``,
|
74
|
+
``add_small_si_prefixes`` and ``add_ppth_form`` options present
|
75
|
+
in :class:`InputOptions`. These options are helper functions which
|
76
|
+
modify the corresponding exponent replacement dictionaries.
|
77
|
+
|
78
|
+
>>> formatter = Formatter(
|
79
|
+
... exp_mode="engineering",
|
80
|
+
... exp_format="prefix",
|
81
|
+
... add_c_prefix=True,
|
82
|
+
... )
|
83
|
+
>>> print(formatter.input_options)
|
84
|
+
InputOptions(
|
85
|
+
'exp_mode': 'engineering',
|
86
|
+
'exp_format': 'prefix',
|
87
|
+
'add_c_prefix': True,
|
88
|
+
)
|
89
|
+
>>> print(formatter.input_options.extra_si_prefixes)
|
90
|
+
None
|
91
|
+
>>> print(formatter.populated_options.extra_si_prefixes)
|
92
|
+
{-2: 'c'}
|
93
|
+
|
94
|
+
""" # noqa: E501
|
95
|
+
|
96
|
+
exp_mode: modes.ExpMode
|
97
|
+
exp_val: int | type(modes.AutoExpVal)
|
98
|
+
round_mode: modes.RoundMode
|
99
|
+
ndigits: int | type(modes.AutoDigits)
|
100
|
+
upper_separator: modes.UpperSeparators
|
101
|
+
decimal_separator: modes.DecimalSeparators
|
102
|
+
lower_separator: modes.LowerSeparators
|
103
|
+
sign_mode: modes.SignMode
|
104
|
+
left_pad_char: modes.LeftPadChar
|
105
|
+
left_pad_dec_place: int
|
106
|
+
exp_format: modes.ExpFormat
|
107
|
+
extra_si_prefixes: dict[int, str]
|
108
|
+
extra_iec_prefixes: dict[int, str]
|
109
|
+
extra_parts_per_forms: dict[int, str]
|
110
|
+
capitalize: bool
|
111
|
+
superscript: bool
|
112
|
+
nan_inf_exp: bool
|
113
|
+
paren_uncertainty: bool
|
114
|
+
pdg_sig_figs: bool
|
115
|
+
left_pad_matching: bool
|
116
|
+
paren_uncertainty_separators: bool
|
117
|
+
pm_whitespace: bool
|
118
|
+
|
119
|
+
def __post_init__(self: PopulatedOptions) -> None:
|
120
|
+
validate_options(self)
|
121
|
+
|
122
|
+
def as_dict(self: PopulatedOptions) -> dict[str, Any]:
|
123
|
+
"""
|
124
|
+
Return a dict representation of the PopulatedOptions.
|
125
|
+
|
126
|
+
This dict can be passed into :class:`Formatter` as ``**kwargs``,
|
127
|
+
possibly after modification. This allows for the possibility of
|
128
|
+
constructing new :class:`Formatter` instances based on old ones.
|
129
|
+
"""
|
130
|
+
return asdict(self)
|
131
|
+
|
132
|
+
def __str__(self: PopulatedOptions) -> str:
|
133
|
+
options_str = pformat(self.as_dict(), width=-1, sort_dicts=False)
|
134
|
+
options_str = options_str.lstrip("{").rstrip("}")
|
135
|
+
options_str = f"PopulatedOptions(\n {options_str},\n)"
|
136
|
+
return options_str
|
@@ -0,0 +1,101 @@
|
|
1
|
+
"""Options validation."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING, get_args
|
6
|
+
|
7
|
+
from sciform import modes
|
8
|
+
|
9
|
+
if TYPE_CHECKING: # pragma: no cover
|
10
|
+
from sciform.options.finalized_options import FinalizedOptions
|
11
|
+
from sciform.options.input_options import InputOptions
|
12
|
+
from sciform.options.populated_options import PopulatedOptions
|
13
|
+
|
14
|
+
|
15
|
+
def validate_options(
|
16
|
+
options: InputOptions | PopulatedOptions | FinalizedOptions,
|
17
|
+
) -> None:
|
18
|
+
"""Validate user inputs."""
|
19
|
+
validate_sig_fig_round_mode(options)
|
20
|
+
validate_exp_val(options)
|
21
|
+
validate_separator_options(options)
|
22
|
+
|
23
|
+
|
24
|
+
def validate_sig_fig_round_mode(
|
25
|
+
options: InputOptions | PopulatedOptions | FinalizedOptions,
|
26
|
+
) -> None:
|
27
|
+
r"""Validate ndigits if round_mode == "sig_fig"."""
|
28
|
+
if (
|
29
|
+
options.round_mode == "sig_fig"
|
30
|
+
and isinstance(options.ndigits, int)
|
31
|
+
and options.ndigits < 1
|
32
|
+
):
|
33
|
+
msg = f"ndigits must be >= 1 for sig fig rounding, not {options.ndigits}."
|
34
|
+
raise ValueError(msg)
|
35
|
+
|
36
|
+
|
37
|
+
def validate_exp_val(
|
38
|
+
options: InputOptions | PopulatedOptions | FinalizedOptions,
|
39
|
+
) -> None:
|
40
|
+
"""Validate exp_val."""
|
41
|
+
if options.exp_val is not modes.AutoExpVal and options.exp_val is not None:
|
42
|
+
if options.exp_mode in ["fixed_point", "percent"] and options.exp_val != 0:
|
43
|
+
msg = (
|
44
|
+
f"Exponent must must be 0, not exp_val={options.exp_val}, for "
|
45
|
+
f"fixed point and percent exponent modes."
|
46
|
+
)
|
47
|
+
raise ValueError(msg)
|
48
|
+
if (
|
49
|
+
options.exp_mode in ["engineering", "engineering_shifted"]
|
50
|
+
and options.exp_val % 3 != 0
|
51
|
+
):
|
52
|
+
msg = (
|
53
|
+
f"Exponent must be a multiple of 3, not exp_val={options.exp_val}, "
|
54
|
+
f"for engineering exponent modes."
|
55
|
+
)
|
56
|
+
raise ValueError(msg)
|
57
|
+
if options.exp_mode == "binary_iec" and options.exp_val % 10 != 0:
|
58
|
+
msg = (
|
59
|
+
f"Exponent must be a multiple of 10, not "
|
60
|
+
f"exp_val={options.exp_val}, for binary IEC exponent mode."
|
61
|
+
)
|
62
|
+
raise ValueError(msg)
|
63
|
+
|
64
|
+
|
65
|
+
def validate_separator_options(
|
66
|
+
options: InputOptions | PopulatedOptions | FinalizedOptions,
|
67
|
+
) -> None:
|
68
|
+
"""Validate separator user input."""
|
69
|
+
if options.upper_separator is not None:
|
70
|
+
if options.upper_separator not in get_args(modes.UpperSeparators):
|
71
|
+
msg = (
|
72
|
+
f"upper_separator must be in "
|
73
|
+
f"{get_args(modes.UpperSeparators)}, not "
|
74
|
+
f"{options.upper_separator}."
|
75
|
+
)
|
76
|
+
raise ValueError(msg)
|
77
|
+
if options.upper_separator == options.decimal_separator:
|
78
|
+
msg = (
|
79
|
+
f"upper_separator and decimal_separator "
|
80
|
+
f"({options.upper_separator}) cannot be equal."
|
81
|
+
)
|
82
|
+
raise ValueError(msg)
|
83
|
+
|
84
|
+
if options.decimal_separator is not None and (
|
85
|
+
options.decimal_separator not in get_args(modes.DecimalSeparators)
|
86
|
+
):
|
87
|
+
msg = (
|
88
|
+
f"decimal_separator must be in "
|
89
|
+
f"{get_args(modes.DecimalSeparators)}, not "
|
90
|
+
f"{options.decimal_separator}."
|
91
|
+
)
|
92
|
+
raise ValueError(msg)
|
93
|
+
|
94
|
+
if options.lower_separator is not None and (
|
95
|
+
options.lower_separator not in get_args(modes.LowerSeparators)
|
96
|
+
):
|
97
|
+
msg = (
|
98
|
+
f"lower_separator must be in {get_args(modes.LowerSeparators)}, "
|
99
|
+
f"not {options.lower_separator}."
|
100
|
+
)
|
101
|
+
raise ValueError(msg)
|
sciform/output_conversion.py
CHANGED
@@ -84,9 +84,9 @@ def convert_sciform_format(
|
|
84
84
|
|
85
85
|
>>> from sciform.output_conversion import convert_sciform_format
|
86
86
|
>>> print(convert_sciform_format("(7.8900 ± 0.0001)×10²", "latex"))
|
87
|
-
(7.8900\:\pm\:0.0001)\times10^{2}
|
87
|
+
$(7.8900\:\pm\:0.0001)\times10^{2}$
|
88
88
|
>>> print(convert_sciform_format("16.18033E+03", "latex"))
|
89
|
-
16.18033\times10^{3}
|
89
|
+
$16.18033\times10^{3}$
|
90
90
|
|
91
91
|
HTML
|
92
92
|
====
|
sciform/scinum.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
from decimal import Decimal
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from sciform.formatting import
|
8
|
+
from sciform.formatting import FormattedNumber, format_from_options
|
9
9
|
from sciform.fsml import format_options_from_fmt_spec
|
10
10
|
|
11
11
|
if TYPE_CHECKING: # pragma: no cover
|
@@ -23,13 +23,13 @@ class SciNum:
|
|
23
23
|
be populated with global default settings at format time.
|
24
24
|
|
25
25
|
>>> from sciform import SciNum
|
26
|
-
>>>
|
27
|
-
>>> print(f"{
|
26
|
+
>>> num = SciNum(12345.54321)
|
27
|
+
>>> print(f"{num:!3f}")
|
28
28
|
12300
|
29
|
-
>>> print(f"{
|
29
|
+
>>> print(f"{num:+2.3R}")
|
30
30
|
+ 12.346E+03
|
31
|
-
>>>
|
32
|
-
>>> print(f"{
|
31
|
+
>>> num = SciNum(123456.654321, 0.0234)
|
32
|
+
>>> print(f"{num:#!2r()}")
|
33
33
|
(0.123456654(23))e+06
|
34
34
|
"""
|
35
35
|
|
@@ -45,12 +45,13 @@ class SciNum:
|
|
45
45
|
else:
|
46
46
|
self.uncertainty = Decimal(str(uncertainty))
|
47
47
|
|
48
|
-
def __format__(self: SciNum, fmt: str) ->
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
def __format__(self: SciNum, fmt: str) -> FormattedNumber:
|
49
|
+
input_options = format_options_from_fmt_spec(fmt)
|
50
|
+
return format_from_options(
|
51
|
+
self.value,
|
52
|
+
self.uncertainty,
|
53
|
+
input_options=input_options,
|
54
|
+
)
|
54
55
|
|
55
56
|
def __repr__(self: SciNum) -> str:
|
56
57
|
if self.uncertainty is not None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sciform
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.34.0
|
4
4
|
Summary: A package for formatting numbers into scientific formatted strings.
|
5
5
|
Author-email: Justin Gerber <justin.gerber48@gmail.com>
|
6
6
|
Project-URL: homepage, https://github.com/jagerber48/sciform
|
@@ -132,18 +132,22 @@ You can view the on-going review
|
|
132
132
|
Usage
|
133
133
|
=====
|
134
134
|
|
135
|
+
Here we provide a few key usage examples.
|
136
|
+
For many more details see
|
137
|
+
`Usage <https://sciform.readthedocs.io/en/stable/usage.html>`_.
|
138
|
+
|
135
139
|
``sciform`` provides a wide variety of formatting options which can be
|
136
140
|
controlled when constructing ``Formatter`` objects which are then used
|
137
141
|
to format numbers into strings according to the selected options.
|
138
142
|
|
139
143
|
>>> from sciform import Formatter
|
140
|
-
>>>
|
144
|
+
>>> formatter = Formatter(
|
141
145
|
... round_mode="dec_place", ndigits=6, upper_separator=" ", lower_separator=" "
|
142
146
|
... )
|
143
|
-
>>> print(
|
147
|
+
>>> print(formatter(51413.14159265359))
|
144
148
|
51 413.141 593
|
145
|
-
>>>
|
146
|
-
>>> print(
|
149
|
+
>>> formatter = Formatter(round_mode="sig_fig", ndigits=4, exp_mode="engineering")
|
150
|
+
>>> print(formatter(123456.78))
|
147
151
|
123.5e+03
|
148
152
|
|
149
153
|
Users can also format numbers by constructing ``SciNum`` objects and
|
@@ -162,11 +166,11 @@ to format pairs of numbers as value/uncertainty pairs.
|
|
162
166
|
This can be done by passing two numbers into a ``Formatter`` call or by
|
163
167
|
using the ``SciNum`` object.
|
164
168
|
|
165
|
-
>>>
|
166
|
-
>>> print(
|
169
|
+
>>> formatter = Formatter(ndigits=2, upper_separator=" ", lower_separator=" ")
|
170
|
+
>>> print(formatter(123456.654321, 0.00345))
|
167
171
|
123 456.654 3 ± 0.003 4
|
168
|
-
>>>
|
169
|
-
>>> print(
|
172
|
+
>>> formatter = Formatter(ndigits=4, exp_mode="engineering")
|
173
|
+
>>> print(formatter(123456.654321, 0.00345))
|
170
174
|
(123.456654321 ± 0.000003450)e+03
|
171
175
|
|
172
176
|
>>> num = SciNum(123456.654321, 0.00345)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
sciform/__init__.py,sha256=S6-oZ4wA-h2iFlpUhrRLDSycdn5b8kRd5s5xGI64-a8,844
|
2
|
+
sciform/format_utils.py,sha256=JNFlSxxM9K3M1KI5oLyTsPPzAsvgKDQAGgxPN3wqG_I,16545
|
3
|
+
sciform/formatter.py,sha256=paVP2I6IO9y7lvkFB7_tMGFikfp7MS9PSlSG5zPWrzQ,15057
|
4
|
+
sciform/formatting.py,sha256=Z2jLIeuqMm6JWd_FurJ_-rU0Yr96jT-tb8CYEzjPgZA,12228
|
5
|
+
sciform/fsml.py,sha256=r22RiBPrkDaiPgE2VQrtGms9aBmWqolymsmmnp-z57s,3378
|
6
|
+
sciform/global_configuration.py,sha256=L3nORbdGOO6ZZk8Xv81lr8S95Od35CgxlpOsdJDxs44,6964
|
7
|
+
sciform/grouping.py,sha256=1hZjjVKRUy477LGGFniZwwEHHBDaSb6E0K3SAeCACVY,1641
|
8
|
+
sciform/modes.py,sha256=8slohyIPHeAdgq3aszoNq9RJl6ibageFqDMxeC9NhrQ,3893
|
9
|
+
sciform/output_conversion.py,sha256=IwylcmSXdfIZlVaa7fplL0E0F6JdtJyoHC_T9ZwGYo8,5489
|
10
|
+
sciform/prefix.py,sha256=1rJeKMs8m97V3ZvMzHikqXLDDINp9aEK0AI3YaCcRMw,596
|
11
|
+
sciform/scinum.py,sha256=oqbXiENMwELQmhVF7sR6fcnBECqDV8GNyOLNIrNIB9I,1857
|
12
|
+
sciform/options/__init__.py,sha256=tw_CZHaNrdixZoQyG6ZD5UMKkQxXok0qBQCxt28Gh20,51
|
13
|
+
sciform/options/conversion.py,sha256=ReqASoGNQIfa-r5-HEGZxokENti_NRkAjRP-zrxY0EU,4124
|
14
|
+
sciform/options/finalized_options.py,sha256=PNcoAiB_WccbqxRJ5o_WPLnqb_n3J_MDpFTKqJxdSBM,1388
|
15
|
+
sciform/options/global_options.py,sha256=8493K2Yu47D8-Dr6rgVWVKAVo-aAVaPzFNEpG7X0IAk,782
|
16
|
+
sciform/options/input_options.py,sha256=gGUhuZr_t89IVhHwpEbtzLV9hvjMXVdMCgwe9OghO4k,3788
|
17
|
+
sciform/options/populated_options.py,sha256=rVExq0YXzpk9kk-KUVm2oGXQEH56jBDGtZZ2vTqLqqo,4999
|
18
|
+
sciform/options/validation.py,sha256=O1UTe5yHA0eN7sTtDsMNBh-1t3C-lbPayGoW9kn7FSU,3527
|
19
|
+
sciform-0.34.0.dist-info/LICENSE,sha256=-oyCEZu-6HLrRSfRg44uuNDE0c59GVEdqYIgidqSP70,1056
|
20
|
+
sciform-0.34.0.dist-info/METADATA,sha256=wZhvWEVrSkst3jLEYmpX5imgbzg0IatMk861S37M0f4,8002
|
21
|
+
sciform-0.34.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
22
|
+
sciform-0.34.0.dist-info/top_level.txt,sha256=mC01YOQ-1u5pDk9BJia-pQKLUMvkreMsldDpFcp7NV8,8
|
23
|
+
sciform-0.34.0.dist-info/RECORD,,
|