boto3-assist 0.4.0__py3-none-any.whl → 0.5.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.
@@ -0,0 +1,286 @@
1
+ import math
2
+ from typing import List, Optional
3
+ from aws_lambda_powertools import Logger
4
+
5
+ logger = Logger()
6
+
7
+
8
+ class NumberUtility:
9
+ """
10
+ Number Utility.
11
+ """
12
+
13
+ @staticmethod
14
+ def is_numeric(value: str | None | int | float) -> bool:
15
+ """
16
+ Determines if a value is a number or not. This will remove any dashes, periods "-", "."
17
+ and then determine if the value is numeric
18
+ Args:
19
+ value (str): string value of a number
20
+
21
+ Returns:
22
+ bool: _description_
23
+ """
24
+ if value is None:
25
+ return False
26
+
27
+ if value is None:
28
+ return False
29
+
30
+ if str(value).isnumeric():
31
+ return True
32
+ try:
33
+ float(value)
34
+ return True
35
+ except: # noqa: E722, pylint: disable=w0702
36
+ return False
37
+
38
+ @staticmethod
39
+ def are_numeric(values: List[int] | List[str] | List[float]) -> bool:
40
+ """determines if a set of values are numeric or not"""
41
+ for value in values:
42
+ if not NumberUtility.is_numeric(value):
43
+ return False
44
+
45
+ return True
46
+
47
+ @staticmethod
48
+ def to_number_or_none(value: str) -> float | int | None:
49
+ """Converts a string to a number."""
50
+ if value is not None and NumberUtility.is_numeric(value):
51
+ if "." in str(value):
52
+ return float(value)
53
+ else:
54
+ return int(value)
55
+ return None
56
+
57
+ @staticmethod
58
+ def to_float(
59
+ value: str | float | int, raise_errors: Optional[bool] = False
60
+ ) -> float:
61
+ """_summary_
62
+
63
+ Args:
64
+ value (str | float | int): _description_
65
+
66
+
67
+
68
+ Returns:
69
+ float: returns a float of zero.
70
+ """
71
+ try:
72
+ return float(value)
73
+ except: # noqa: E722, pylint: disable=w0702
74
+ logger.error(f"Unable to convert {value} to float")
75
+ if raise_errors:
76
+ raise
77
+ return 0.0
78
+
79
+ @staticmethod
80
+ def get_max_length(items: List[str] | List[int] | List[float]) -> int:
81
+ """Returns the max length of an item in a list."""
82
+ length = 0
83
+ for item in items:
84
+ if len(str(item)) > length:
85
+ length = len(str(item))
86
+
87
+ return length
88
+
89
+ @staticmethod
90
+ def to_significant_digits_rounded(value, significant_digits=10):
91
+ """
92
+ converts to significant digits
93
+ """
94
+ result = math.floor(value * 10**significant_digits) / 10**significant_digits
95
+ to_the_right = len(f"{int(value)}")
96
+ f_string_number = (
97
+ f"{value:.{significant_digits + to_the_right}g}" # Using f-string
98
+ )
99
+ result = float(f_string_number)
100
+
101
+ return result
102
+
103
+ @staticmethod
104
+ def get_number_of_decimal_places(number: float) -> int:
105
+ """
106
+ Gets the number decimal places
107
+
108
+ Args:
109
+ number (float): the number to inspect
110
+
111
+ Returns:
112
+ int: number of decimal places
113
+ """
114
+ number_str = f"{number}"
115
+ if "." in number_str:
116
+ to_the_right = number_str.split(".")[1]
117
+ return len(to_the_right)
118
+
119
+ return 0
120
+
121
+ @staticmethod
122
+ def to_significant_digits(number: float, significant_digits: int = 10):
123
+ """To Significat Digits"""
124
+ # make sure we're dealing with a number
125
+ number = float(number)
126
+ if significant_digits < 0:
127
+ raise ValueError("Decimal places must be non-negative")
128
+ else:
129
+ number_str = f"{number}"
130
+ if "." in number_str:
131
+ decimal_point_index = number_str.index(".")
132
+ number_of_decimal_places = NumberUtility.get_number_of_decimal_places(
133
+ number
134
+ )
135
+ if number_of_decimal_places > significant_digits:
136
+ return NumberUtility.to_significant_digits_rounded(
137
+ number, significant_digits
138
+ )
139
+ else:
140
+ cutoff_index = decimal_point_index + significant_digits + 1
141
+ truncated_str = number_str[:cutoff_index]
142
+ return float(truncated_str)
143
+ else:
144
+ return number
145
+
146
+ @staticmethod
147
+ def is_decimal(value):
148
+ is_numeric = NumberUtility.is_numeric(value)
149
+ contains_decimal = "." in str(value)
150
+
151
+ return is_numeric and contains_decimal
152
+
153
+ @staticmethod
154
+ def percent_difference(number1, number2):
155
+ """
156
+ Calculate the percent difference between two numbers.
157
+
158
+ Parameters:
159
+ - number1: The first number.
160
+ - number2: The second number.
161
+
162
+ Returns:
163
+ - The percent difference between the two numbers.
164
+ """
165
+ number1 = float(number1)
166
+ number2 = float(number2)
167
+ # Calculate the absolute difference between the two numbers
168
+ difference = abs(number1 - number2)
169
+
170
+ # Calculate the average of the two numbers
171
+ average = (number1 + number2) / 2
172
+
173
+ if difference > 0:
174
+ # Calculate the percent difference
175
+ percent_diff = (difference / average) * 100
176
+
177
+ return percent_diff
178
+
179
+ return 0.0
180
+
181
+ @staticmethod
182
+ def to_significant_figure(
183
+ number: int | float | str, sig_figs: int
184
+ ) -> int | float | str:
185
+ """
186
+ Formats a number to it's significant figures.
187
+ Examples
188
+ 12345.6789, 4 = 12350
189
+
190
+ Args:
191
+ number (int | float | str): a valid number
192
+ sig_figs (int): the number of signigicant figures
193
+
194
+ Returns:
195
+ int | float: the value after applying a significant figure
196
+ """
197
+ # just used for tracking
198
+ original_value = number
199
+ value: int | float | str = 0
200
+ if str(number).lower() == "nan":
201
+ return number
202
+
203
+ if NumberUtility.is_numeric(number) and isinstance(number, str):
204
+ if "." in str(number):
205
+ number = float(number)
206
+ else:
207
+ number = int(number)
208
+
209
+ if number == 0:
210
+ if sig_figs > 1:
211
+ value = "0." + "0" * (sig_figs - 1)
212
+ else:
213
+ value = 0
214
+
215
+ else:
216
+ scale = int(math.floor(math.log10(abs(float(number)))))
217
+ pre_power = int(scale - sig_figs + 1)
218
+
219
+ factor = 10 ** (pre_power)
220
+ rounded = round(number / factor) * factor
221
+
222
+ if not isinstance(rounded, int):
223
+ value = f"{rounded:.{sig_figs}g}"
224
+ else:
225
+ value = f"{rounded}"
226
+
227
+ if "." in f"{value}" and len(f"{value}") >= (sig_figs):
228
+ value = float(value)
229
+ elif "." in f"{value}" and len(f"{value}") <= (sig_figs + 1):
230
+ # there are more sig figures in the length of the figures
231
+ # adding 1 to account for the decimal place
232
+ value = float(value)
233
+ else:
234
+ # due to the scientific express we need to float it first
235
+ value = float(value)
236
+ value = int(value)
237
+
238
+ logger.debug(
239
+ {
240
+ "source": "",
241
+ "sig": sig_figs,
242
+ "value": {"original": original_value, "converted": value},
243
+ }
244
+ )
245
+
246
+ return value
247
+
248
+ @staticmethod
249
+ def get_significant_figure(value) -> int:
250
+ """
251
+ Calculate the number of significant figures in a number.
252
+ Removes leading and trailing zeros for float numbers.
253
+ """
254
+ if not value:
255
+ return 0
256
+ if isinstance(value, int) or isinstance(value, int):
257
+ return len(str(value).strip("0"))
258
+ elif isinstance(value, float):
259
+ if value == 0:
260
+ return 0
261
+ else:
262
+ length: int = 0
263
+ if value > 1:
264
+ value_str = f"{value}"
265
+ digits = value_str
266
+ number = digits.split(".")
267
+ left = number[0].lstrip("0")
268
+ right = ""
269
+ if len(number) > 1:
270
+ right = f"{number[1].rstrip('0')}"
271
+
272
+ digits_stripped = f"{left}{right}"
273
+ # Remove decimal point and trailing zeros
274
+ length = len(digits_stripped)
275
+
276
+ else:
277
+ value_str = f"{value}"
278
+ # remove the "." remove all left and right decimals
279
+ # example: 0.0012 becomes 12 with sig digit of 2
280
+ digits_stripped = value_str.replace(".", "").rstrip("0").lstrip("0")
281
+ length = len(digits_stripped)
282
+
283
+ return length
284
+
285
+ else:
286
+ return 0
@@ -7,11 +7,13 @@ MIT License. See Project Root for the license information.
7
7
  import hashlib
8
8
  import secrets
9
9
  import string
10
+ import time
10
11
  from datetime import datetime
11
12
  from decimal import Decimal
12
13
  import uuid
13
14
  import json
14
15
  from aws_lambda_powertools import Logger
16
+ from boto3_assist.utilities.datetime_utility import DatetimeUtility
15
17
 
16
18
  logger = Logger()
17
19
 
@@ -230,3 +232,60 @@ class StringUtility:
230
232
  size = int(len(_bytes))
231
233
 
232
234
  return size
235
+
236
+ @staticmethod
237
+ def generate_sortable_uuid():
238
+ """
239
+ Generates a unique id for the execution event
240
+ """
241
+ epoch_time = time.time()
242
+ sortable_uuid: uuid.UUID = DatetimeUtility.uuid1_utc(timestamp=epoch_time)
243
+
244
+ time_stamp = str(epoch_time).replace(".", "-")
245
+ sortable_id = f"{time_stamp}:{str(sortable_uuid)}"
246
+ return sortable_id
247
+
248
+ @staticmethod
249
+ def to_bool(value: str | bool | int | None) -> bool:
250
+ """
251
+ Converts a string or boolean value to a boolean.
252
+
253
+ Args:
254
+ value (str | bool | int | None): The value to convert.
255
+
256
+ Returns:
257
+ bool: The converted boolean value.
258
+
259
+ Raises:
260
+ ValueError: If the input value is not a valid boolean or string representation.
261
+ """
262
+ return StringUtility.to_boolean(value)
263
+
264
+ @staticmethod
265
+ def to_boolean(value: str | bool | int | None) -> bool:
266
+ """
267
+ Converts a string or boolean value to a boolean.
268
+
269
+ Args:
270
+ value (str | bool | int | None): The value to convert.
271
+
272
+ Returns:
273
+ bool: The converted boolean value.
274
+
275
+ Raises:
276
+ ValueError: If the input value is not a valid boolean or string representation.
277
+ """
278
+ if isinstance(value, bool):
279
+ return value
280
+ if isinstance(value, str):
281
+ value = str(value).lower().strip()
282
+ if value in ("true", "1", "t", "y", "yes"):
283
+ return True
284
+ if value in ("false", "0", "f", "n", "no"):
285
+ return False
286
+ elif isinstance(value, int):
287
+ return bool(value)
288
+ elif value is None:
289
+ return False
290
+ else:
291
+ raise ValueError(f"Invalid boolean value: {value}")
boto3_assist/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.4.0'
1
+ __version__ = '0.5.0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boto3_assist
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Additional boto3 wrappers to make your life a little easier
5
5
  Author-email: Eric Wilson <boto3-assist@geekcafe.com>
6
6
  License-File: LICENSE-EXPLAINED.txt
@@ -1,9 +1,9 @@
1
1
  boto3_assist/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- boto3_assist/boto3session.py,sha256=NWhWtYR3143thEbTpoklkwdz77-fTMs-QsoQdqfRm6E,6430
3
- boto3_assist/connection.py,sha256=EJlGueLIYqMSKs7aQlThK1S0Zkb8dOYBWch1iRZdgUI,3233
4
- boto3_assist/connection_tracker.py,sha256=_s1t7h2DOi3CCIHIr_HIKyGjku65WR-HJ_v8vJHDvO0,2977
2
+ boto3_assist/boto3session.py,sha256=Q9sByNC0r_aMQfHnIEnxtTaiCMUqikm8UeSTxV7-Np0,6632
3
+ boto3_assist/connection.py,sha256=CNGkAIRyfrELoWrV0ziQBA3oHacNFuLL3i8faUPRiO0,3486
4
+ boto3_assist/connection_tracker.py,sha256=bfImvNVX-0Lhb-ombOurWUpNLdI0qVDil-kokBdIFkY,4345
5
5
  boto3_assist/http_status_codes.py,sha256=G0zRSWenwavYKETvDF9tNVUXQz3Ae2gXdBETYbjvJe8,3284
6
- boto3_assist/version.py,sha256=2eiWQI55fd-roDdkt4Hvl9WzrTJ4xQo33VzFud6D03U,22
6
+ boto3_assist/version.py,sha256=0PaI2eSOCp5kkNcpKpUSbLHf66rL9xQzFpYyLGpEtyM,22
7
7
  boto3_assist/aws_lambda/event_info.py,sha256=OkZ4WzuGaHEu_T8sB188KBgShAJhZpWASALKRGBOhMg,14648
8
8
  boto3_assist/cloudwatch/cloudwatch_connection.py,sha256=mnGWaLSQpHh5EeY7Ek_2o9JKHJxOELIYtQVMX1IaHn4,2480
9
9
  boto3_assist/cloudwatch/cloudwatch_connection_tracker.py,sha256=mzRtO1uHrcfJNh1XrGEiXdTqxwEP8d1RqJkraMNkgK0,410
@@ -11,10 +11,12 @@ boto3_assist/cloudwatch/cloudwatch_log_connection.py,sha256=qQMZHjUJ6gA8wU9utjQh
11
11
  boto3_assist/cloudwatch/cloudwatch_logs.py,sha256=VtI0OnFjX1l4RYVvA8tvveGkPwAogtrplnflZ4dQSNM,1204
12
12
  boto3_assist/cloudwatch/cloudwatch_query.py,sha256=uNhSb1Gfp99v8BaHmCnCKs63j4MMU4WveqBavCJyhGY,6409
13
13
  boto3_assist/cognito/cognito_authorizer.py,sha256=ONcxzjTACgVYl6qI9kJAQ5SoRMtVHYGDeuKi5QqJvOY,5837
14
+ boto3_assist/cognito/cognito_connection.py,sha256=deuXR3cNHz0mCYff2k0LfAvK--9OkqehWp0Bl--lDuw,1607
15
+ boto3_assist/cognito/cognito_utility.py,sha256=kjzd2PcCCP9GbxlMlgYEIQE1FSyPPjsRPjr6-kN08Jc,18238
14
16
  boto3_assist/cognito/jwks_cache.py,sha256=1Y9r-YfQ8qrgZN5xYPvjUEEV0vthbdcPdAIaPbZP7kU,373
17
+ boto3_assist/cognito/user.py,sha256=H8Sgu1tSkd22flwGkAa_9Im2jYw5ra7YhVJdtcoFMOs,623
15
18
  boto3_assist/dynamodb/dynamodb.py,sha256=q3U4uYqnKX1_u5TXv8Iq94JrBad16q0rL_vKxQyR_3k,15772
16
- boto3_assist/dynamodb/dynamodb_connection.py,sha256=hr4IGbbEE73fh375tj3_XtYAwwDV0s7z1-6JK_1Tc30,5263
17
- boto3_assist/dynamodb/dynamodb_connection_tracker.py,sha256=0BWHRfi5_vjkJLuCSX6sYwvA6wc7BSYCQnGrzbhfyKA,404
19
+ boto3_assist/dynamodb/dynamodb_connection.py,sha256=x6Ylb_uVAY5TS0AIBUNOSyywKIqros3xX8diLTjZUsc,3275
18
20
  boto3_assist/dynamodb/dynamodb_helpers.py,sha256=ajpTJ5bJOm9PDgE2Zx9p2zkTRFV4xswqJRS81SOTn1s,12198
19
21
  boto3_assist/dynamodb/dynamodb_importer.py,sha256=nCKsyRQeMqDSf0Q5mQ_X_oVIg4PRnu0hcUzZnBli610,3471
20
22
  boto3_assist/dynamodb/dynamodb_index.py,sha256=D0Lq121qk1cXeMetPeqnzvv6CXd0XfEygfdUXaljLG8,8551
@@ -27,21 +29,23 @@ boto3_assist/dynamodb/dynamodb_reserved_words.py,sha256=p0irNBSqGe4rd2FwWQqbRJWr
27
29
  boto3_assist/dynamodb/dynamodb_reserved_words.txt,sha256=rvctS63Cv3i9SHmPq2Unmj6RZyQ-OMqxUXsNhtbg1is,4136
28
30
  boto3_assist/dynamodb/readme.md,sha256=wNMzdRwk0qRV0kE88UUYnJos3pEK0HNjEIVkq2PATf8,1490
29
31
  boto3_assist/dynamodb/troubleshooting.md,sha256=uGpBaBUt_MyzjzwFOLOe0udTgcvaOpiTFxfj7ilLNkM,136
30
- boto3_assist/ec2/ec2_connection.py,sha256=VT_hnu_behs9mf1Prggo5U9c9KAwbnLBTSZkmu4gp30,3277
32
+ boto3_assist/ec2/ec2_connection.py,sha256=IrtaidH6_SF5l3OeNehRsTlC-sX7EURVqcO-U6P6ff8,1318
31
33
  boto3_assist/environment_services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- boto3_assist/environment_services/environment_loader.py,sha256=jvG1xwMtgkZqu70NbjG1b1IefKiWgaidjZZoqsSfULk,3370
34
+ boto3_assist/environment_services/environment_loader.py,sha256=zCA4mRdVWMLKzjDRvrJbhQfRVP4HAMGpuQFi07zzULk,3396
33
35
  boto3_assist/environment_services/environment_variables.py,sha256=4ccBKdPt6O7hcRT3zBHd8vqu8yQU8udmoD5RLAT3iMs,6801
34
36
  boto3_assist/errors/custom_exceptions.py,sha256=zC2V2Y4PUtKj3uLPn8mB-JessksKWJWvKM9kp1dmvt8,760
35
37
  boto3_assist/s3/s3.py,sha256=DFCJs5z1mMIT8nZfnqPyr_cvhi9-FePuYH--tzD7b5E,17104
36
- boto3_assist/s3/s3_connection.py,sha256=fMD1FxQazJ9gSFDrRmrv7YJE5QnCesGoWC1b5Dba_Jo,3774
37
- boto3_assist/utilities/datetime_utility.py,sha256=TbqGQkJDTahqvaZAIV550nhYnW1Bsq0Hdu3Go6P4RRs,10282
38
+ boto3_assist/s3/s3_connection.py,sha256=FI1AhZV4UbTXQRTb4TqL9mv88Gt018rPZVFBvLetVAw,2163
39
+ boto3_assist/utilities/datetime_utility.py,sha256=dgAMB9VqakrYIPXlSoVQiLSsc_yhrJK4gMfJO9mX90w,11112
40
+ boto3_assist/utilities/dictionaroy_utility.py,sha256=PjUrerEd6uhmw37A-k7xe_DWHvXZGGoMqT6xjUqmWBI,893
38
41
  boto3_assist/utilities/file_operations.py,sha256=Zy8fu8fpuVNf7U9NimrLdy5FRF71XSI159cnRdzmzGY,3411
39
42
  boto3_assist/utilities/http_utility.py,sha256=koFv7Va-8ng-47Nt1K2Sh7Ti95e62IYs9VMLlGh9Kt4,1173
40
43
  boto3_assist/utilities/logging_utility.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ boto3_assist/utilities/numbers_utility.py,sha256=KIiNkBSRbfNWvtXG5SdHp625LTiW12VtADUa4ZlWMFo,8709
41
45
  boto3_assist/utilities/serialization_utility.py,sha256=vsipCQZu91hApkJ8tKRTxBa8kNRvn06ED-isquqAVQQ,5744
42
- boto3_assist/utilities/string_utility.py,sha256=fzsZkldRYtdkIqR0kD-TlHH4fh4iMtYvo_U3UNZhN7U,7427
43
- boto3_assist-0.4.0.dist-info/METADATA,sha256=6PglW0CMu-LSfBs74ViaxrykaJG96KtCspwIIPcP0PU,1728
44
- boto3_assist-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- boto3_assist-0.4.0.dist-info/licenses/LICENSE-EXPLAINED.txt,sha256=WFREvTpfTjPjDHpOLADxJpCKpIla3Ht87RUUGii4ODU,606
46
- boto3_assist-0.4.0.dist-info/licenses/LICENSE.txt,sha256=PXDhFWS5L5aOTkVhNvoitHKbAkgxqMI2uUPQyrnXGiI,1105
47
- boto3_assist-0.4.0.dist-info/RECORD,,
46
+ boto3_assist/utilities/string_utility.py,sha256=sBY80aQO-fTRanlHryZFMQBxdo6OvLRvnZjZrQepHlI,9283
47
+ boto3_assist-0.5.0.dist-info/METADATA,sha256=PnVWeXj0gPTylAWn97RKzRoTN1bS6YSZdk4yRETGJaY,1728
48
+ boto3_assist-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
49
+ boto3_assist-0.5.0.dist-info/licenses/LICENSE-EXPLAINED.txt,sha256=WFREvTpfTjPjDHpOLADxJpCKpIla3Ht87RUUGii4ODU,606
50
+ boto3_assist-0.5.0.dist-info/licenses/LICENSE.txt,sha256=PXDhFWS5L5aOTkVhNvoitHKbAkgxqMI2uUPQyrnXGiI,1105
51
+ boto3_assist-0.5.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- """
2
- Geek Cafe, LLC
3
- Maintainers: Eric Wilson
4
- MIT License. See Project Root for the license information.
5
- """
6
-
7
- from boto3_assist.connection_tracker import ConnectionTracker
8
-
9
-
10
- class DynamoDBConnectionTracker(ConnectionTracker):
11
- """
12
- Tracks DynamoDB Connection Requests.
13
- Useful in for performance tuning and debugging.
14
- """
15
-
16
- def __init__(self) -> None:
17
- super().__init__("DynamoDB")