From f0aa788c78aebe9328ea647f91371abd124a5cc4 Mon Sep 17 00:00:00 2001 From: David Michaels Date: Wed, 7 Aug 2024 15:12:09 -0400 Subject: [PATCH] Added to_number function to misc_utils. --- dcicutils/misc_utils.py | 21 ++++++++++++++------- dcicutils/structured_data.py | 4 ++-- pyproject.toml | 2 +- test/test_misc_utils.py | 8 +++++--- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/dcicutils/misc_utils.py b/dcicutils/misc_utils.py index 7143706f7..f11a1603c 100644 --- a/dcicutils/misc_utils.py +++ b/dcicutils/misc_utils.py @@ -1022,17 +1022,17 @@ def to_integer(value: str, fallback: Optional[Any] = None, strict: bool = False) def to_number(value: str, allow_commas: bool = False, allow_multiplier_suffix: bool = False, - as_float: bool = False, + allow_float: bool = False, fallback: Optional[Union[int, float]] = None) -> Optional[Union[int, float]]: """ - Converts the give string value to an int, or float if as_float is True. + Converts the give string value to an int, or possibly float if allow_float is True. If allow_commas is True (default: False) then allow commas (only) every three digits. If allow_multiplier_suffix is True (default: False) allow any of K, Kb, KB; or M, Mb, MB; or G, Gb, or GB; or T, Tb, TB, to mean multiply value by one thousand; one million; - one billion; or one trillion; respectively. If as_float is True (default: False) + one billion; or one trillion; respectively. If allow_float is True (default: False) allow the value to be floating point (i.e. with a decimal point and a fractional part), - in which case the returned value will be of type float rather than int. + in which case the returned value will be of type float, if it needs to be, and not int. If the string is not well formated then returns None. """ if not (isinstance(value, str) and (value := value.strip())): @@ -1058,10 +1058,11 @@ def to_number(value: str, return fallback break - if as_float is True: + if allow_float is True: if (dot_index := value.rfind(".")) >= 0: if value_fraction := value[dot_index + 1:].strip(): try: + # value_fraction = float(f"0.{value_fraction}") value_fraction = float(f"0.{value_fraction}") except Exception: return fallback @@ -1075,15 +1076,21 @@ def to_number(value: str, if not value.isdigit(): return fallback - result = int(value) * value_multiplier + result = int(value) if value_fraction: result += value_fraction + result *= value_multiplier + if value_negative: result = -result - return float(result) if as_float is True else result + if allow_float is True: + if result == int(result): + result = int(result) + + return result def to_float(value: str, fallback: Optional[Any] = None) -> Optional[Any]: diff --git a/dcicutils/structured_data.py b/dcicutils/structured_data.py index 8891b28d0..b1d57c600 100644 --- a/dcicutils/structured_data.py +++ b/dcicutils/structured_data.py @@ -727,7 +727,7 @@ def _map_function_integer(self, typeinfo: dict) -> Callable: allow_multiplier_suffix = typeinfo.get("allow_multiplier_suffix") is True def map_integer(value: str, src: Optional[str]) -> Any: # noqa nonlocal allow_commas, allow_multiplier_suffix - return to_number(value, fallback=value, as_float=False, + return to_number(value, fallback=value, allow_float=False, allow_commas=allow_commas, allow_multiplier_suffix=allow_multiplier_suffix) return map_integer @@ -737,7 +737,7 @@ def _map_function_number(self, typeinfo: dict) -> Callable: allow_multiplier_suffix = typeinfo.get("allow_multiplier_suffix") is True def map_number(value: str, src: Optional[str]) -> Any: # noqa nonlocal allow_commas, allow_multiplier_suffix - return to_number(value, fallback=value, as_float=True, + return to_number(value, fallback=value, allow_float=True, allow_commas=allow_commas, allow_multiplier_suffix=allow_multiplier_suffix) return map_number diff --git a/pyproject.toml b/pyproject.toml index b69bed90c..b76de206c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dcicutils" -version = "8.13.3.1b22" # TODO: To become 8.14.0 +version = "8.13.3.1b23" # TODO: To become 8.14.0 description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources" authors = ["4DN-DCIC Team "] license = "MIT" diff --git a/test/test_misc_utils.py b/test/test_misc_utils.py index 44ae1931e..83216da5d 100644 --- a/test/test_misc_utils.py +++ b/test/test_misc_utils.py @@ -3733,11 +3733,11 @@ def test_to_number(): assert to_number("1234") == 1234 assert to_number("1,234,567") is None assert to_number("27500") == 27500 - assert to_number("789", as_float=True) == 789.0 - assert type(to_number("789", as_float=True)) == float + assert to_number("789", allow_float=True) == 789.0 + # assert type(to_number("789", allow_float=True)) == float assert to_number("27500", allow_commas=True) == 27500 assert to_number("1,234,567", allow_commas=True) == 1234567 - assert to_number("1234.0567", as_float=True) == 1234.0567 + assert to_number("1234.0567", allow_float=True) == 1234.0567 assert to_number("1K", allow_multiplier_suffix=True) == 1000 assert to_number("1Kb", allow_multiplier_suffix=True) == 1000 assert to_number("1KB", allow_multiplier_suffix=True) == 1000 @@ -3756,5 +3756,7 @@ def test_to_number(): assert to_number("-1,234,567K", allow_commas=True, allow_multiplier_suffix=True) == -1234567000 assert to_number(4321) == 4321 assert to_number(4321.1234) == 4321.1234 + assert to_number("1.5K", allow_multiplier_suffix=True, allow_float=True) == 1500 + assert type(to_number("1.5K", allow_multiplier_suffix=True, allow_float=True)) == int # TODO: More ... pass