Skip to content

Commit

Permalink
Feature/unittests (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
jagerber48 authored Aug 27, 2023
2 parents d301157 + 3f159f1 commit 3194205
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 30 deletions.
16 changes: 11 additions & 5 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ Unreleased
Now the brackets and ``'%'`` symbol will be omitted unless
``nan_inf_exp=True``.
* In ``latex=True`` mode there is now a space between the number and a
prefix or part-per translated exponent. For value/uncertainty
formatting the space is still absent. For ``latex=False`` there is
still always a space for number and value/uncertainty formatting
before the translated exponent string.
prefix or parts-per translated exponent.
For value/uncertainty formatting the space is still absent.
For ``latex=False`` there is still always a space for number and
value/uncertainty formatting before the translated exponent string.
* In ``latex=True`` mode ``'nan'`` and ``'inf'`` strings are now wrapped
in ``'\text{}'``.
* Refactor code for resolving the exponent string.
* Refactored code for resolving exponent strings.
* Added more unit tests to reach 100% test coverage. Mostly added test
cases for invalid internal inputs.
* Raise ``NotImplementedError`` when attempting value/uncertainty
formatting with binary exponent modes.
Rounding and truncating are not properly implemented in binary mode
yet.

0.27.4 (2023-08-25)
-------------------
Expand Down
8 changes: 4 additions & 4 deletions src/sciform/format_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def parse_standard_exp_str(exp_str: str) -> tuple[int, int]:
base = 10
elif exp_symb.lower() == 'b':
base = 2
else:
else: # pragma: no cover
assert False, 'unreachable'

exp_val = int(f'{exp_sign}{exp_digits}')
Expand Down Expand Up @@ -269,8 +269,8 @@ def get_pdg_round_digit(num: Decimal) -> int:
e.g. 123.45632 +/- 0.987 would be rounded as 123.5 +/- 1.0.
'''
round_digit = top_digit
else:
raise ValueError
else: # pragma: no cover
assert False, "unreachable"

return round_digit

Expand All @@ -293,7 +293,7 @@ def get_round_digit(num: Decimal,
else:
round_digit = -ndigits
else:
raise TypeError(f'Unhandled round mode: {round_mode}.')
raise ValueError(f'Unhandled round mode: {round_mode}.')
return round_digit


Expand Down
4 changes: 4 additions & 0 deletions src/sciform/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def format_num(num: Decimal, unrendered_options: FormatOptions) -> str:
def format_val_unc(val: Decimal, unc: Decimal,
unrendered_options: FormatOptions):
options = unrendered_options.render()
if (options.exp_mode is ExpMode.BINARY
or options.exp_mode is ExpMode.BINARY_IEC):
raise NotImplementedError('Binary exponent modes are not supported '
'for value/uncertainty formatting.')

if options.round_mode is RoundMode.DEC_PLACE:
warn('Precision round mode not available for value/uncertainty '
Expand Down
32 changes: 13 additions & 19 deletions src/sciform/modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,11 @@ class FillMode(Enum):
ZERO = 'zero'

def to_char(self) -> str:
if self is FillMode.SPACE:
return ' '
elif self is FillMode.ZERO:
return '0'
else:
raise ValueError(f'Invalid fill mode: {self}')
char_dict = {
FillMode.SPACE: ' ',
FillMode.ZERO: '0'
}
return char_dict[self]


class SignMode(Enum):
Expand Down Expand Up @@ -87,19 +86,14 @@ class GroupingSeparator(Enum):
SPACE = 'space'

def to_char(self) -> str:
if self is GroupingSeparator.NONE:
return ''
elif self is GroupingSeparator.COMMA:
return ','
elif self is GroupingSeparator.POINT:
return '.'
elif self is GroupingSeparator.UNDERSCORE:
return '_'
elif self is GroupingSeparator.SPACE:
return ' '
else:
raise ValueError(f'Invalid grouping separator: '
f'{self}')
char_dict = {
GroupingSeparator.NONE: '',
GroupingSeparator.COMMA: ',',
GroupingSeparator.POINT: '.',
GroupingSeparator.UNDERSCORE: '_',
GroupingSeparator.SPACE: ' '
}
return char_dict[self]


UpperGroupingSeparators = Literal[GroupingSeparator.NONE,
Expand Down
11 changes: 10 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
set_global_defaults, reset_global_defaults,
global_add_c_prefix, global_add_small_si_prefixes,
global_add_ppth_form, global_reset_si_prefixes,
global_reset_parts_per_forms)
global_reset_parts_per_forms, global_reset_iec_prefixes)


class TestConfig(unittest.TestCase):
Expand Down Expand Up @@ -55,6 +55,15 @@ def test_small_si_prefixes(self):
self.assertEqual(num_str, expected_num_str)
global_reset_si_prefixes()

def test_iec_prefix(self):
num = SciNum(1024)
fmt_spec = 'bp'
self.assertEqual(f'{num:{fmt_spec}}', '1 Ki')
set_global_defaults(FormatOptions(extra_iec_prefixes={10: 'KiB'}))
self.assertEqual(f'{num:{fmt_spec}}', '1 KiB')
global_reset_iec_prefixes()
self.assertEqual(f'{num:{fmt_spec}}', '1 Ki')

def test_ppth_form(self):
num = 0.0024
formatter = Formatter(FormatOptions(
Expand Down
7 changes: 7 additions & 0 deletions tests/test_float_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def test_superscript_exp(self):
(FormatOptions(exp_mode=ExpMode.SCIENTIFIC,
superscript_exp=True), '7.89×10²')
]),

# Superscript in prefix mode when there's no replacement
(789, [
(FormatOptions(exp_mode=ExpMode.SCIENTIFIC,
exp_format=ExpFormat.PREFIX,
superscript_exp=True), '7.89×10²')
]),
(1024, [
(FormatOptions(exp_mode=ExpMode.BINARY,
superscript_exp=True), '1×2¹⁰')
Expand Down
100 changes: 99 additions & 1 deletion tests/test_invalid_options.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import unittest
from decimal import Decimal

from sciform import (Formatter, FormatOptions, RoundMode, ExpMode,
GroupingSeparator)
GroupingSeparator, ExpFormat)
from sciform.formatting import format_non_finite
from sciform.grouping import add_group_chars_between_numbers, add_separators
from sciform.format_utils import (
get_top_digit, get_mantissa_exp_base, get_exp_str, get_sign_str,
get_round_digit, get_prefix_dict, parse_standard_exp_str
)


class TestInvalidOptions(unittest.TestCase):
Expand Down Expand Up @@ -120,3 +127,94 @@ def test_upper_decimal_separator_comma(self):
upper_separator=GroupingSeparator.COMMA,
decimal_separator=GroupingSeparator.COMMA
)

def test_format_non_finite(self):
self.assertRaises(ValueError, format_non_finite, Decimal(1.0),
FormatOptions().render())

def test_add_group_chars(self):
self.assertRaises(ValueError, add_group_chars_between_numbers,
string='123456.654321', group_char='_',
direction='forwards', group_size=3)

def test_add_separators(self):
self.assertRaises(ValueError, add_separators,
num_str='123.456.789',
upper_separator=',',
decimal_separator='.',
lower_separator='_',
group_size=3)

def test_get_top_digit_infinite(self):
self.assertEqual(get_top_digit(Decimal('nan')), 0)

def test_get_mantissa_exp_base_fixed_point_set_exp(self):
self.assertRaises(ValueError, get_mantissa_exp_base,
num=Decimal(3),
exp_mode=ExpMode.FIXEDPOINT,
input_exp_val=1)

def test_get_mantissa_exp_base_engineering_set_exp(self):
self.assertRaises(ValueError, get_mantissa_exp_base,
num=Decimal(3),
exp_mode=ExpMode.ENGINEERING,
input_exp_val=1)

def test_get_mantissa_exp_base_binary_iec_set_exp(self):
self.assertRaises(ValueError, get_mantissa_exp_base,
num=Decimal(3),
exp_mode=ExpMode.BINARY_IEC,
input_exp_val=3)

def test_get_mantissa_exp_base_bad_exp_mode(self):
self.assertRaises(ValueError, get_mantissa_exp_base,
num=Decimal(3),
exp_mode='eng',
input_exp_val=3)

def test_get_exp_str_bad_exp_mode(self):
self.assertRaises(ValueError, get_exp_str,
exp_val=2,
exp_mode='sci',
exp_format=ExpFormat.STANDARD,
capitalize=False,
latex=False,
latex_trim_whitespace=False,
superscript=False,
extra_si_prefixes={},
extra_iec_prefixes={},
extra_parts_per_forms={})

def test_get_sign_str_bad_sign_mode(self):
self.assertRaises(ValueError, get_sign_str,
num=Decimal(1),
sign_mode='space')

def test_get_round_digit_bad_round_mode(self):
self.assertRaises(ValueError, get_round_digit,
num=Decimal(123.456),
round_mode='none',
ndigits=0)

def test_get_prefix_dict_bad_base(self):
self.assertRaises(ValueError, get_prefix_dict,
exp_format=ExpFormat.PREFIX,
base=3,
extra_si_prefixes={},
extra_iec_prefixes={},
extra_parts_per_forms={})

def test_get_prefix_dict_bad_format(self):
self.assertRaises(ValueError, get_prefix_dict,
exp_format='pref',
base=10,
extra_si_prefixes={},
extra_iec_prefixes={},
extra_parts_per_forms={})

def test_parse_standard_exp_str_binary(self):
"""
This is the only place that this is tested while binary
value/uncertainty is not implemented.
"""
self.assertEqual(parse_standard_exp_str('b+10'), (2, 10))
13 changes: 13 additions & 0 deletions tests/test_val_unc_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ def test_pdg(self):

self.run_val_unc_formatter_cases(cases_list)

def test_binary_not_implemented(self):
sform = Formatter(FormatOptions(exp_mode=ExpMode.BINARY))
self.assertRaises(NotImplementedError, sform, 1024, 32)

@unittest.expectedFailure
def test_binary(self):
"""
This test should pass when binary value/uncertainty formatting
is implemented
"""
sform = Formatter(FormatOptions(exp_mode=ExpMode.BINARY))
self.assertEqual(sform(1024, 32), '(1.00000 +/- 0.03125)b+10')

def test_pdg_ndigits_error(self):
self.assertRaises(ValueError, FormatOptions, pdg_sig_figs=True,
ndigits=0)
Expand Down

0 comments on commit 3194205

Please sign in to comment.