dcicutils 8.13.3.1b25__py3-none-any.whl → 8.13.3.1b27__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.
dcicutils/misc_utils.py CHANGED
@@ -986,57 +986,73 @@ def str_to_bool(x: Optional[str]) -> Optional[bool]:
986
986
  raise ValueError(f"An argument to str_or_bool must be a string or None: {x!r}")
987
987
 
988
988
 
989
- def to_integer(value: str, fallback: Optional[Any] = None, strict: bool = False) -> Optional[Any]:
990
- try:
991
- return int(value)
992
- except Exception:
993
- if strict is not True:
994
- try:
995
- return int(float(value))
996
- except Exception:
997
- pass
998
- return fallback
999
-
1000
-
1001
- _MULTIPLIER_K = 1000
1002
- _MULTIPLIER_M = 1000 * _MULTIPLIER_K
1003
- _MULTIPLIER_G = 1000 * _MULTIPLIER_M
1004
- _MULTIPLIER_T = 1000 * _MULTIPLIER_G
1005
-
1006
- _MULTIPLIER_SUFFIXES = {
1007
- "K": _MULTIPLIER_K,
1008
- "KB": _MULTIPLIER_K,
1009
- "M": _MULTIPLIER_M,
1010
- "MB": _MULTIPLIER_M,
1011
- "G": _MULTIPLIER_G,
1012
- "GB": _MULTIPLIER_G,
1013
- "T": _MULTIPLIER_T,
1014
- "TB": _MULTIPLIER_T
989
+ def to_integer(value: str,
990
+ allow_commas: bool = False,
991
+ allow_multiplier_suffix: bool = False,
992
+ fallback: Optional[Union[int, float]] = None) -> Optional[int]:
993
+ return to_number(value, fallback=fallback, as_float=False,
994
+ allow_commas=allow_commas,
995
+ allow_multiplier_suffix=allow_multiplier_suffix)
996
+
997
+
998
+ def to_float(value: str,
999
+ allow_commas: bool = False,
1000
+ allow_multiplier_suffix: bool = False,
1001
+ fallback: Optional[Union[int, float]] = None) -> Optional[int]:
1002
+ return to_number(value, fallback=fallback, as_float=True,
1003
+ allow_commas=allow_commas,
1004
+ allow_multiplier_suffix=allow_multiplier_suffix)
1005
+
1006
+
1007
+ _TO_NUMBER_MULTIPLIER_K = 1000
1008
+ _TO_NUMBER_MULTIPLIER_M = 1000 * _TO_NUMBER_MULTIPLIER_K
1009
+ _TO_NUMBER_MULTIPLIER_G = 1000 * _TO_NUMBER_MULTIPLIER_M
1010
+ _TO_NUMBER_MULTIPLIER_T = 1000 * _TO_NUMBER_MULTIPLIER_G
1011
+
1012
+ _TO_NUMBER_MULTIPLIER_SUFFIXES = {
1013
+ "K": _TO_NUMBER_MULTIPLIER_K,
1014
+ "KB": _TO_NUMBER_MULTIPLIER_K,
1015
+ "M": _TO_NUMBER_MULTIPLIER_M,
1016
+ "MB": _TO_NUMBER_MULTIPLIER_M,
1017
+ "G": _TO_NUMBER_MULTIPLIER_G,
1018
+ "GB": _TO_NUMBER_MULTIPLIER_G,
1019
+ "T": _TO_NUMBER_MULTIPLIER_T,
1020
+ "TB": _TO_NUMBER_MULTIPLIER_T
1015
1021
  }
1016
1022
 
1017
1023
 
1018
1024
  def to_number(value: str,
1025
+ as_float: bool = False,
1019
1026
  allow_commas: bool = False,
1020
1027
  allow_multiplier_suffix: bool = False,
1021
- allow_float: bool = False,
1022
1028
  fallback: Optional[Union[int, float]] = None) -> Optional[Union[int, float]]:
1023
-
1024
1029
  """
1025
- Converts the give string value to an int, or possibly float if allow_float is True.
1026
- If allow_commas is True (default: False) then allow commas (only) every three digits.
1027
- If allow_multiplier_suffix is True (default: False) allow any of K, Kb, KB; or M, Mb, MB;
1028
- or G, Gb, or GB; or T, Tb, TB, to mean multiply value by one thousand; one million;
1029
- one billion; or one trillion; respectively. If allow_float is True (default: False)
1030
- allow the value to be floating point (i.e. with a decimal point and a fractional part),
1031
- in which case the returned value will be of type float, if it needs to be, and not int.
1032
- If the string is not well formated then returns None.
1030
+ Converts the given string value to an int, or float if as_float is True,
1031
+ or None or the give fallback value if malformed.
1032
+
1033
+ If allow_commas is True then allows appropriately placed commas (i.e. every three digits).
1034
+
1035
+ If allow_multiplier_suffix is True allows any of the multiplier suffixes K, KB; M, MB;
1036
+ or G, GB; or T, TB; all case-insensitive; which will multiply the value by a thousand;
1037
+ a million; a billion; or a trillion; respectively.
1038
+
1039
+ If as_float is True then value is parsed and returned as a float rather than int.
1040
+
1041
+ Note that even if as_float is False, values that might LOOK like a float, can be
1042
+ an int, for example, "1.5K", returns 1500 as an int since it is; but "1.5002K"
1043
+ is malformed, since 1.5002K is equivalent to 1500.2 which is not an int.
1033
1044
  """
1045
+
1034
1046
  if not (isinstance(value, str) and (value := value.strip())):
1035
- return value if isinstance(value, (int, float)) else fallback
1047
+ # Just in case the value is already the actual/desired return type.
1048
+ if as_float is True:
1049
+ return float(value) if isinstance(value, (float, int)) else fallback
1050
+ else:
1051
+ return value if isinstance(value, int) else fallback
1036
1052
 
1037
1053
  value_multiplier = 1
1038
- value_negative = False
1039
1054
  value_fraction = None
1055
+ value_negative = False
1040
1056
 
1041
1057
  if value.startswith("-"):
1042
1058
  if not (value := value[1:].strip()):
@@ -1048,23 +1064,31 @@ def to_number(value: str,
1048
1064
 
1049
1065
  if allow_multiplier_suffix is True:
1050
1066
  value_upper = value.upper()
1051
- for suffix in _MULTIPLIER_SUFFIXES:
1067
+ for suffix in _TO_NUMBER_MULTIPLIER_SUFFIXES:
1052
1068
  if value_upper.endswith(suffix):
1053
- value_multiplier *= _MULTIPLIER_SUFFIXES[suffix]
1069
+ value_multiplier *= _TO_NUMBER_MULTIPLIER_SUFFIXES[suffix]
1054
1070
  if not (value := value[:-len(suffix)].strip()):
1055
1071
  return fallback
1056
1072
  break
1057
1073
 
1058
- if allow_float is True:
1074
+ if (allow_multiplier_suffix is True) or (as_float is True):
1075
+ # Allow for example "1.5K" to mean 1500 (integer).
1059
1076
  if (dot_index := value.rfind(".")) >= 0:
1060
1077
  if value_fraction := value[dot_index + 1:].strip():
1061
1078
  try:
1062
1079
  value_fraction = float(f"0.{value_fraction}")
1063
1080
  except Exception:
1064
1081
  return fallback
1065
- value = value[:dot_index].strip()
1082
+ if not (value := value[:dot_index].strip()):
1083
+ if not value_fraction:
1084
+ return fallback
1085
+ value = "0"
1086
+ elif (as_float is not True) and (value_dot_zero_suffix := re.search(r"\.0*$", value)):
1087
+ # Allow for example "123.00" to mean 123 (integer).
1088
+ value = value[:value_dot_zero_suffix.start()]
1066
1089
 
1067
1090
  if (allow_commas is True) and ("," in value):
1091
+ # Make sure that any commas are properly placed.
1068
1092
  if not re.fullmatch(r"(-?\d{1,3}(,\d{3})*)", value):
1069
1093
  return fallback
1070
1094
  value = value.replace(",", "")
@@ -1072,28 +1096,31 @@ def to_number(value: str,
1072
1096
  if not value.isdigit():
1073
1097
  return fallback
1074
1098
 
1075
- result = int(value)
1099
+ value = float(value) if as_float is True else int(value)
1076
1100
 
1077
1101
  if value_fraction:
1078
- result += value_fraction
1079
-
1080
- result *= value_multiplier
1102
+ value_float = float(value) + value_fraction
1103
+ # Here we do NOT simply do: value_float *= float(value_multiplier);
1104
+ # because it introduces obvious FLOATing point precision ERRORs; for example,
1105
+ # to_integer("1.5678K", allow_multiplier_suffix=True) would yield 1567.8000000000002
1106
+ # if we simply did 1.5678 * 1000.0; but doing the multiplication 10 at a time obviates this
1107
+ # idiosyncracy yielding 1567.8; this ASSUMES that the multipliers are simple multiples of 10.
1108
+ while value_multiplier > 1:
1109
+ value_float *= 10
1110
+ value_multiplier /= 10
1111
+ if as_float is True:
1112
+ value = value_float
1113
+ else:
1114
+ value = int(value_float)
1115
+ if value_float != value:
1116
+ return fallback
1117
+ else:
1118
+ value *= value_multiplier
1081
1119
 
1082
1120
  if value_negative:
1083
- result = -result
1084
-
1085
- if allow_float is True:
1086
- if result == int(result):
1087
- result = int(result)
1121
+ value = -value
1088
1122
 
1089
- return result
1090
-
1091
-
1092
- def to_float(value: str, fallback: Optional[Any] = None) -> Optional[Any]:
1093
- try:
1094
- return float(value)
1095
- except Exception:
1096
- return fallback
1123
+ return value
1097
1124
 
1098
1125
 
1099
1126
  def to_boolean(value: str, fallback: Optional[Any]) -> Optional[Any]:
@@ -13,7 +13,7 @@ from dcicutils.data_readers import CsvReader, Excel, RowReader
13
13
  from dcicutils.datetime_utils import normalize_date_string, normalize_datetime_string
14
14
  from dcicutils.misc_utils import (create_dict, create_readonly_object, is_uuid, load_json_if,
15
15
  merge_objects, remove_empty_properties, right_trim, split_string,
16
- to_boolean, to_enum, to_integer, to_number, VirtualApp)
16
+ to_boolean, to_enum, to_float, to_integer, VirtualApp)
17
17
  from dcicutils.portal_object_utils import PortalObject
18
18
  from dcicutils.portal_utils import Portal as PortalBase
19
19
  from dcicutils.submitr.progress_constants import PROGRESS_PARSE as PROGRESS
@@ -727,9 +727,9 @@ class Schema(SchemaBase):
727
727
  allow_multiplier_suffix = typeinfo.get("allow_multiplier_suffix") is True
728
728
  def map_integer(value: str, src: Optional[str]) -> Any: # noqa
729
729
  nonlocal allow_commas, allow_multiplier_suffix
730
- return to_number(value, fallback=value, allow_float=False,
731
- allow_commas=allow_commas,
732
- allow_multiplier_suffix=allow_multiplier_suffix)
730
+ return to_integer(value, fallback=value,
731
+ allow_commas=allow_commas,
732
+ allow_multiplier_suffix=allow_multiplier_suffix)
733
733
  return map_integer
734
734
 
735
735
  def _map_function_number(self, typeinfo: dict) -> Callable:
@@ -737,9 +737,9 @@ class Schema(SchemaBase):
737
737
  allow_multiplier_suffix = typeinfo.get("allow_multiplier_suffix") is True
738
738
  def map_number(value: str, src: Optional[str]) -> Any: # noqa
739
739
  nonlocal allow_commas, allow_multiplier_suffix
740
- return to_number(value, fallback=value, allow_float=True,
741
- allow_commas=allow_commas,
742
- allow_multiplier_suffix=allow_multiplier_suffix)
740
+ return to_float(value, fallback=value,
741
+ allow_commas=allow_commas,
742
+ allow_multiplier_suffix=allow_multiplier_suffix)
743
743
  return map_number
744
744
 
745
745
  def _map_function_string(self, typeinfo: dict) -> Callable:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.13.3.1b25
3
+ Version: 8.13.3.1b27
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -45,7 +45,7 @@ dcicutils/license_policies/park-lab-gpl-pipeline.jsonc,sha256=vLZkwm3Js-kjV44nug
45
45
  dcicutils/license_policies/park-lab-pipeline.jsonc,sha256=9qlY0ASy3iUMQlr3gorVcXrSfRHnVGbLhkS427UaRy4,283
46
46
  dcicutils/license_utils.py,sha256=2Yxnh1T1iuMe6wluwbvpFO_zYSGPxB4-STAMc-vz-YM,47202
47
47
  dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
48
- dcicutils/misc_utils.py,sha256=4Kj_0llvUabA1O-n4cGkFdDq_shq_b8iDFDpL9AAIkQ,112934
48
+ dcicutils/misc_utils.py,sha256=R3nUtmx4nynmdTfc98w3CV2JKYCPZPRBijets1nWM20,114877
49
49
  dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
50
50
  dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
51
51
  dcicutils/portal_object_utils.py,sha256=Az3n1aL-PQkN5gOFE6ZqC2XkYsqiwKlq7-tZggs1QN4,11062
@@ -66,7 +66,7 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
66
66
  dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
67
67
  dcicutils/snapshot_utils.py,sha256=YDeI3vD-MhAtHwKDzfEm2q-n3l-da2yRpRR3xp0Ah1M,23021
68
68
  dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
69
- dcicutils/structured_data.py,sha256=OfGV92SxUosCuh3hwvRJGwreGx1Q43q73mryIZurrxk,66157
69
+ dcicutils/structured_data.py,sha256=LN-Bn-RUM_jy0T6s-ovZT9QXDsQ8w1_QVtkb8LAHFs4,66119
70
70
  dcicutils/submitr/progress_constants.py,sha256=5bxyX77ql8qEJearfHEvsvXl7D0GuUODW0T65mbRmnE,2895
71
71
  dcicutils/submitr/ref_lookup_strategy.py,sha256=VJN-Oo0LLna6Vo2cu47eC-eU-yUC9NFlQP29xajejVU,4741
72
72
  dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
@@ -75,8 +75,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
75
75
  dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
76
76
  dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
77
77
  dcicutils/zip_utils.py,sha256=_Y9EmL3D2dUZhxucxHvrtmmlbZmK4FpSsHEb7rGSJLU,3265
78
- dcicutils-8.13.3.1b25.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
79
- dcicutils-8.13.3.1b25.dist-info/METADATA,sha256=SUAgwGUivRxlaR7vnZQEmmCaCcjBWeU7M2KySPMDmwo,3440
80
- dcicutils-8.13.3.1b25.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
81
- dcicutils-8.13.3.1b25.dist-info/entry_points.txt,sha256=W6kEWdUJk9tQ4myAgpehPdebcwvCAZ7UgB-wyPgDUMg,335
82
- dcicutils-8.13.3.1b25.dist-info/RECORD,,
78
+ dcicutils-8.13.3.1b27.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
79
+ dcicutils-8.13.3.1b27.dist-info/METADATA,sha256=w4K_ZFbf-pBM62p35V5NoZS4HQWx0jlimtBQ2z73pQE,3440
80
+ dcicutils-8.13.3.1b27.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
81
+ dcicutils-8.13.3.1b27.dist-info/entry_points.txt,sha256=W6kEWdUJk9tQ4myAgpehPdebcwvCAZ7UgB-wyPgDUMg,335
82
+ dcicutils-8.13.3.1b27.dist-info/RECORD,,