boto3-assist 0.4.0__py3-none-any.whl → 0.5.1__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.
- boto3_assist/boto3session.py +3 -0
- boto3_assist/cognito/cognito_connection.py +59 -0
- boto3_assist/cognito/cognito_utility.py +514 -0
- boto3_assist/cognito/user.py +27 -0
- boto3_assist/connection.py +14 -8
- boto3_assist/connection_tracker.py +75 -35
- boto3_assist/dynamodb/dynamodb_connection.py +11 -61
- boto3_assist/ec2/ec2_connection.py +12 -61
- boto3_assist/environment_services/environment_loader.py +1 -1
- boto3_assist/models/serializable_model.py +30 -0
- boto3_assist/s3/s3_connection.py +10 -51
- boto3_assist/utilities/datetime_utility.py +26 -0
- boto3_assist/utilities/dictionaroy_utility.py +26 -0
- boto3_assist/utilities/numbers_utility.py +286 -0
- boto3_assist/utilities/serialization_utility.py +68 -26
- boto3_assist/utilities/string_utility.py +59 -0
- boto3_assist/version.py +1 -1
- {boto3_assist-0.4.0.dist-info → boto3_assist-0.5.1.dist-info}/METADATA +1 -1
- {boto3_assist-0.4.0.dist-info → boto3_assist-0.5.1.dist-info}/RECORD +22 -17
- boto3_assist/dynamodb/dynamodb_connection_tracker.py +0 -17
- {boto3_assist-0.4.0.dist-info → boto3_assist-0.5.1.dist-info}/WHEEL +0 -0
- {boto3_assist-0.4.0.dist-info → boto3_assist-0.5.1.dist-info}/licenses/LICENSE-EXPLAINED.txt +0 -0
- {boto3_assist-0.4.0.dist-info → boto3_assist-0.5.1.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,80 +1,120 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Geek Cafe, LLC
|
|
3
3
|
Maintainers: Eric Wilson
|
|
4
|
-
MIT License.
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import traceback
|
|
8
8
|
import os
|
|
9
|
+
from typing import Dict
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class ConnectionTracker:
|
|
12
13
|
"""
|
|
13
|
-
Tracks
|
|
14
|
-
|
|
14
|
+
Tracks Boto3 connection requests for performance tuning and debugging.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
__stack_trace_env_var (str): Environment variable name to enable stack trace logging.
|
|
18
|
+
__issue_stack_trace (bool | None): Caches the result of whether stack trace logging is enabled.
|
|
19
|
+
__connection_counter (Dict[str, int]): Tracks the number of connections per service.
|
|
15
20
|
"""
|
|
16
21
|
|
|
17
|
-
def __init__(self
|
|
22
|
+
def __init__(self) -> None:
|
|
18
23
|
self.__stack_trace_env_var: str = "BOTO3_ASSIST_CONNECTION_STACK_TRACE"
|
|
19
|
-
self.__connection_counter: int = 0
|
|
20
|
-
self.__service_name: str = service_name
|
|
21
24
|
self.__issue_stack_trace: bool | None = None
|
|
25
|
+
self.__connection_counter: Dict[str, int] = {}
|
|
26
|
+
|
|
27
|
+
def add(self, service_name: str) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Increments the connection count for a given service and
|
|
30
|
+
performs a check on the number of connections.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
service_name (str): Name of the AWS service.
|
|
34
|
+
"""
|
|
35
|
+
self.__connection_counter[service_name] = (
|
|
36
|
+
self.__connection_counter.get(service_name, 0) + 1
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self.check(service_name=service_name)
|
|
22
40
|
|
|
23
41
|
@property
|
|
24
42
|
def issue_stack_trace(self) -> bool:
|
|
25
|
-
"""
|
|
43
|
+
"""
|
|
44
|
+
Checks if stack trace logging is enabled by the environment variable.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
bool: True if stack trace logging is enabled, False otherwise.
|
|
48
|
+
"""
|
|
26
49
|
if self.__issue_stack_trace is None:
|
|
27
50
|
self.__issue_stack_trace = (
|
|
28
51
|
os.getenv(self.__stack_trace_env_var, "").lower() == "true"
|
|
29
52
|
)
|
|
30
53
|
return self.__issue_stack_trace
|
|
31
54
|
|
|
32
|
-
def
|
|
33
|
-
"""
|
|
34
|
-
|
|
55
|
+
def check(self, service_name: str) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Checks the connection count for a service and logs warnings if needed.
|
|
35
58
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
Args:
|
|
60
|
+
service_name (str): Name of the AWS service.
|
|
61
|
+
"""
|
|
62
|
+
connection_count = self.__connection_counter.get(service_name, 0)
|
|
63
|
+
if connection_count > 1:
|
|
64
|
+
service_message = (
|
|
65
|
+
f"Your {service_name} service has more than one connection.\n"
|
|
66
|
+
)
|
|
41
67
|
|
|
42
68
|
if not self.issue_stack_trace:
|
|
43
69
|
stack_trace_message = (
|
|
44
|
-
f"\nTo add
|
|
45
|
-
f"
|
|
70
|
+
f"\nTo add additional information to the log and determine where additional connections are being created, "
|
|
71
|
+
f"set the environment variable {self.__stack_trace_env_var} to true.\n"
|
|
46
72
|
)
|
|
47
73
|
else:
|
|
48
74
|
stack = "\n".join(traceback.format_stack())
|
|
49
75
|
stack_trace_message = (
|
|
50
|
-
f"\nStack Trace
|
|
51
|
-
f"\n{stack}"
|
|
76
|
+
f"\nStack Trace Enabled with {self.__stack_trace_env_var}\n{stack}"
|
|
52
77
|
)
|
|
53
78
|
|
|
54
79
|
self.__log_warning(
|
|
55
80
|
f"{service_message}"
|
|
56
|
-
f"Your boto3 connection count is {
|
|
57
|
-
"Under most circumstances you should be able to use the same connection "
|
|
58
|
-
"
|
|
59
|
-
"If you are seeing
|
|
60
|
-
"connections.
|
|
81
|
+
f"Your boto3 connection count is {connection_count}. "
|
|
82
|
+
"Under most circumstances, you should be able to use the same connection "
|
|
83
|
+
"instead of creating a new one. Connections are expensive in terms of time and latency. "
|
|
84
|
+
"If you are seeing performance issues, check how and where you are creating your "
|
|
85
|
+
"connections. You should be able to pass the connection to your other objects "
|
|
61
86
|
"and reuse your boto3 connections."
|
|
87
|
+
"\n\nMOCK Testing may show this message as well, in which case you can dismiss this warning.\n\n"
|
|
62
88
|
f"{stack_trace_message}"
|
|
63
89
|
)
|
|
64
90
|
|
|
65
|
-
def decrement_connection(self) -> None:
|
|
66
|
-
"""
|
|
67
|
-
|
|
91
|
+
def decrement_connection(self, service_name: str) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Decrements the connection count for a service.
|
|
68
94
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"""
|
|
72
|
-
|
|
95
|
+
Args:
|
|
96
|
+
service_name (str): Name of the AWS service.
|
|
97
|
+
"""
|
|
98
|
+
if (
|
|
99
|
+
service_name in self.__connection_counter
|
|
100
|
+
and self.__connection_counter[service_name] > 0
|
|
101
|
+
):
|
|
102
|
+
self.__connection_counter[service_name] -= 1
|
|
73
103
|
|
|
74
|
-
def reset(self) -> None:
|
|
75
|
-
"""
|
|
76
|
-
|
|
104
|
+
def reset(self, service_name: str) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Resets the connection count for a service to zero.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
service_name (str): Name of the AWS service.
|
|
110
|
+
"""
|
|
111
|
+
self.__connection_counter[service_name] = 0
|
|
77
112
|
|
|
78
113
|
def __log_warning(self, message: str) -> None:
|
|
79
|
-
"""
|
|
114
|
+
"""
|
|
115
|
+
Logs a warning message.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
message (str): The warning message to log.
|
|
119
|
+
"""
|
|
80
120
|
print(f"Warning: {message}")
|
|
@@ -8,11 +8,8 @@ from typing import Optional
|
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
from aws_lambda_powertools import Logger
|
|
11
|
-
from boto3_assist.
|
|
12
|
-
|
|
13
|
-
EnvironmentVariables,
|
|
14
|
-
)
|
|
15
|
-
from boto3_assist.dynamodb.dynamodb_connection_tracker import DynamoDBConnectionTracker
|
|
11
|
+
from boto3_assist.connection import Connection
|
|
12
|
+
|
|
16
13
|
|
|
17
14
|
if TYPE_CHECKING:
|
|
18
15
|
from mypy_boto3_dynamodb import DynamoDBClient, DynamoDBServiceResource
|
|
@@ -22,10 +19,9 @@ else:
|
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
logger = Logger()
|
|
25
|
-
tracker: DynamoDBConnectionTracker = DynamoDBConnectionTracker()
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
class DynamoDBConnection:
|
|
24
|
+
class DynamoDBConnection(Connection):
|
|
29
25
|
"""DB Environment"""
|
|
30
26
|
|
|
31
27
|
def __init__(
|
|
@@ -37,66 +33,20 @@ class DynamoDBConnection:
|
|
|
37
33
|
aws_access_key_id: Optional[str] = None,
|
|
38
34
|
aws_secret_access_key: Optional[str] = None,
|
|
39
35
|
) -> None:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
self.aws_secret_access_key = (
|
|
49
|
-
aws_secret_access_key
|
|
50
|
-
or EnvironmentVariables.AWS.DynamoDB.aws_secret_access_key()
|
|
36
|
+
super().__init__(
|
|
37
|
+
service_name="dynamodb",
|
|
38
|
+
aws_profile=aws_profile,
|
|
39
|
+
aws_region=aws_region,
|
|
40
|
+
aws_access_key_id=aws_access_key_id,
|
|
41
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
42
|
+
aws_end_point_url=aws_end_point_url,
|
|
51
43
|
)
|
|
52
|
-
|
|
44
|
+
|
|
53
45
|
self.__dynamodb_client: DynamoDBClient | None = None
|
|
54
46
|
self.__dynamodb_resource: DynamoDBServiceResource | None = None
|
|
55
47
|
|
|
56
48
|
self.raise_on_error: bool = True
|
|
57
49
|
|
|
58
|
-
def setup(self, setup_source: Optional[str] = None) -> None:
|
|
59
|
-
"""
|
|
60
|
-
Setup the environment. Automatically called via init.
|
|
61
|
-
You can run setup at anytime with new parameters.
|
|
62
|
-
Args: setup_source: Optional[str] = None
|
|
63
|
-
Defines the source of the setup. Useful for logging.
|
|
64
|
-
Returns: None
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
logger.info(
|
|
68
|
-
{
|
|
69
|
-
"metric_filter": "db_connection_setup",
|
|
70
|
-
"source": "DynamoDBConnection",
|
|
71
|
-
"aws_profile": self.aws_profile,
|
|
72
|
-
"aws_region": self.aws_region,
|
|
73
|
-
"setup_source": setup_source,
|
|
74
|
-
}
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
self.__session = Boto3SessionManager(
|
|
78
|
-
service_name="dynamodb",
|
|
79
|
-
aws_profile=self.aws_profile,
|
|
80
|
-
aws_region=self.aws_region,
|
|
81
|
-
aws_access_key_id=self.aws_access_key_id,
|
|
82
|
-
aws_secret_access_key=self.aws_secret_access_key,
|
|
83
|
-
aws_endpoint_url=self.end_point_url,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
tracker.increment_connection()
|
|
87
|
-
|
|
88
|
-
self.raise_on_error = EnvironmentVariables.AWS.DynamoDB.raise_on_error_setting()
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def session(self) -> Boto3SessionManager:
|
|
92
|
-
"""Session"""
|
|
93
|
-
if self.__session is None:
|
|
94
|
-
self.setup(setup_source="session init")
|
|
95
|
-
|
|
96
|
-
if self.__session is None:
|
|
97
|
-
raise RuntimeError("Session is not available")
|
|
98
|
-
return self.__session
|
|
99
|
-
|
|
100
50
|
@property
|
|
101
51
|
def client(self) -> DynamoDBClient:
|
|
102
52
|
"""DynamoDB Client Connection"""
|
|
@@ -8,24 +8,20 @@ from typing import Optional
|
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
from aws_lambda_powertools import Logger
|
|
11
|
-
|
|
12
|
-
from boto3_assist.
|
|
13
|
-
EnvironmentVariables,
|
|
14
|
-
)
|
|
15
|
-
from boto3_assist.connection_tracker import ConnectionTracker
|
|
11
|
+
|
|
12
|
+
from boto3_assist.connection import Connection
|
|
16
13
|
|
|
17
14
|
if TYPE_CHECKING:
|
|
18
15
|
from mypy_boto3_ec2 import Client
|
|
19
16
|
else:
|
|
20
17
|
Client = object
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
|
|
23
20
|
logger = Logger()
|
|
24
|
-
tracker: ConnectionTracker = ConnectionTracker(service_name=SERVICE_NAME)
|
|
25
21
|
|
|
26
22
|
|
|
27
|
-
class EC2Connection:
|
|
28
|
-
"""
|
|
23
|
+
class EC2Connection(Connection):
|
|
24
|
+
"""EC2 Connection"""
|
|
29
25
|
|
|
30
26
|
def __init__(
|
|
31
27
|
self,
|
|
@@ -35,69 +31,24 @@ class EC2Connection:
|
|
|
35
31
|
aws_access_key_id: Optional[str] = None,
|
|
36
32
|
aws_secret_access_key: Optional[str] = None,
|
|
37
33
|
) -> None:
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
super().__init__(
|
|
35
|
+
service_name="ec2",
|
|
36
|
+
aws_profile=aws_profile,
|
|
37
|
+
aws_region=aws_region,
|
|
38
|
+
aws_access_key_id=aws_access_key_id,
|
|
39
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
40
|
+
)
|
|
40
41
|
|
|
41
|
-
self.aws_access_key_id = aws_access_key_id
|
|
42
|
-
self.aws_secret_access_key = aws_secret_access_key
|
|
43
|
-
self.__session: Boto3SessionManager | None = None
|
|
44
42
|
self.__client: Client | None = None
|
|
45
43
|
|
|
46
44
|
self.raise_on_error: bool = True
|
|
47
45
|
|
|
48
|
-
def setup(self, setup_source: Optional[str] = None) -> None:
|
|
49
|
-
"""
|
|
50
|
-
Setup the environment. Automatically called via init.
|
|
51
|
-
You can run setup at anytime with new parameters.
|
|
52
|
-
Args: setup_source: Optional[str] = None
|
|
53
|
-
Defines the source of the setup. Useful for logging.
|
|
54
|
-
Returns: None
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
logger.info(
|
|
58
|
-
{
|
|
59
|
-
"metric_filter": "connection_setup",
|
|
60
|
-
"source": "setup",
|
|
61
|
-
"aws_profile": self.aws_profile,
|
|
62
|
-
"aws_region": self.aws_region,
|
|
63
|
-
"setup_source": setup_source,
|
|
64
|
-
}
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# lazy load the session
|
|
68
|
-
self.__session = Boto3SessionManager(
|
|
69
|
-
service_name=SERVICE_NAME,
|
|
70
|
-
aws_profile=self.aws_profile,
|
|
71
|
-
aws_region=self.aws_region or EnvironmentVariables.AWS.region(),
|
|
72
|
-
aws_access_key_id=self.aws_access_key_id
|
|
73
|
-
or EnvironmentVariables.AWS.aws_access_key_id(),
|
|
74
|
-
aws_secret_access_key=self.aws_secret_access_key
|
|
75
|
-
or EnvironmentVariables.AWS.aws_secret_access_key(),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
tracker.increment_connection()
|
|
79
|
-
|
|
80
|
-
self.raise_on_error = EnvironmentVariables.AWS.DynamoDB.raise_on_error_setting()
|
|
81
|
-
|
|
82
|
-
@property
|
|
83
|
-
def session(self) -> Boto3SessionManager:
|
|
84
|
-
"""Session"""
|
|
85
|
-
if self.__session is None:
|
|
86
|
-
self.setup(setup_source="session init")
|
|
87
|
-
|
|
88
|
-
if self.__session is None:
|
|
89
|
-
raise RuntimeError("Session is not available")
|
|
90
|
-
return self.__session
|
|
91
|
-
|
|
92
46
|
@property
|
|
93
47
|
def client(self) -> Client:
|
|
94
48
|
"""Client Connection"""
|
|
95
49
|
if self.__client is None:
|
|
96
|
-
logger.info("Creating Client")
|
|
97
50
|
self.__client = self.session.client
|
|
98
51
|
|
|
99
|
-
if self.raise_on_error and self.__client is None:
|
|
100
|
-
raise RuntimeError("Client is not available")
|
|
101
52
|
return self.__client
|
|
102
53
|
|
|
103
54
|
@client.setter
|
|
@@ -96,7 +96,7 @@ class EnvironmentLoader:
|
|
|
96
96
|
paths: List[str] = []
|
|
97
97
|
for parent in range(parents):
|
|
98
98
|
path = Path(starting_path).parents[parent].absolute()
|
|
99
|
-
|
|
99
|
+
logger.debug(f"searching for {file_name} in: {path}")
|
|
100
100
|
tmp = os.path.join(path, file_name)
|
|
101
101
|
paths.append(tmp)
|
|
102
102
|
if os.path.exists(tmp):
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geek Cafe, LLC
|
|
3
|
+
Maintainers: Eric Wilson
|
|
4
|
+
MIT License. See Project Root for the license information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
from boto3_assist.utilities.serialization_utility import Serialization
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SerializableModel:
|
|
13
|
+
"""Library to Serialize object to a DynamoDB Format"""
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T", bound="SerializableModel")
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def map(source: dict | object, target: T, coerce: bool = True) -> T:
|
|
19
|
+
"""
|
|
20
|
+
Map the source dictionary to the target object.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
- source: The dictionary to map from.
|
|
24
|
+
- target: The object to map to.
|
|
25
|
+
"""
|
|
26
|
+
mapped = Serialization.map(source, target, coerce=coerce)
|
|
27
|
+
if mapped is None:
|
|
28
|
+
raise ValueError("Unable to map source to target")
|
|
29
|
+
|
|
30
|
+
return mapped
|
boto3_assist/s3/s3_connection.py
CHANGED
|
@@ -12,7 +12,7 @@ from boto3_assist.boto3session import Boto3SessionManager
|
|
|
12
12
|
from boto3_assist.environment_services.environment_variables import (
|
|
13
13
|
EnvironmentVariables,
|
|
14
14
|
)
|
|
15
|
-
from boto3_assist.
|
|
15
|
+
from boto3_assist.connection import Connection
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from mypy_boto3_s3 import S3Client, S3ServiceResource
|
|
@@ -22,10 +22,9 @@ else:
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
logger = Logger()
|
|
25
|
-
tracker: ConnectionTracker = ConnectionTracker(service_name="s3")
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
class S3Connection:
|
|
27
|
+
class S3Connection(Connection):
|
|
29
28
|
"""Connection"""
|
|
30
29
|
|
|
31
30
|
def __init__(
|
|
@@ -37,64 +36,24 @@ class S3Connection:
|
|
|
37
36
|
aws_access_key_id: Optional[str] = None,
|
|
38
37
|
aws_secret_access_key: Optional[str] = None,
|
|
39
38
|
) -> None:
|
|
40
|
-
|
|
41
|
-
self.aws_region = aws_region or EnvironmentVariables.AWS.region()
|
|
42
|
-
self.end_point_url = (
|
|
43
|
-
aws_end_point_url or EnvironmentVariables.AWS.endpoint_url()
|
|
44
|
-
)
|
|
45
|
-
self.aws_access_key_id = (
|
|
46
|
-
aws_access_key_id or EnvironmentVariables.AWS.aws_access_key_id()
|
|
47
|
-
)
|
|
48
|
-
self.aws_secret_access_key = (
|
|
49
|
-
aws_secret_access_key or EnvironmentVariables.AWS.aws_secret_access_key()
|
|
50
|
-
)
|
|
51
|
-
self.__session: Boto3SessionManager | None = None
|
|
52
|
-
self.__client: S3Client | None = None
|
|
53
|
-
self.__resource: S3ServiceResource | None = None
|
|
54
|
-
|
|
55
|
-
self.raise_on_error: bool = True
|
|
56
|
-
|
|
57
|
-
def setup(self, setup_source: Optional[str] = None) -> None:
|
|
58
|
-
"""
|
|
59
|
-
Setup the environment. Automatically called via init.
|
|
60
|
-
You can run setup at anytime with new parameters.
|
|
61
|
-
Args: setup_source: Optional[str] = None
|
|
62
|
-
Defines the source of the setup. Useful for logging.
|
|
63
|
-
Returns: None
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
self.__session = Boto3SessionManager(
|
|
39
|
+
super().__init__(
|
|
67
40
|
service_name="s3",
|
|
68
|
-
aws_profile=
|
|
69
|
-
aws_region=
|
|
70
|
-
aws_access_key_id=
|
|
71
|
-
aws_secret_access_key=
|
|
72
|
-
|
|
41
|
+
aws_profile=aws_profile,
|
|
42
|
+
aws_region=aws_region,
|
|
43
|
+
aws_access_key_id=aws_access_key_id,
|
|
44
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
45
|
+
aws_end_point_url=aws_end_point_url,
|
|
73
46
|
)
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
self.raise_on_error = False
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def session(self) -> Boto3SessionManager:
|
|
81
|
-
"""Session"""
|
|
82
|
-
if self.__session is None:
|
|
83
|
-
self.setup(setup_source="session init")
|
|
84
|
-
|
|
85
|
-
if self.__session is None:
|
|
86
|
-
raise RuntimeError("Session is not available")
|
|
87
|
-
return self.__session
|
|
48
|
+
self.__client: S3Client | None = None
|
|
49
|
+
self.__resource: S3ServiceResource | None = None
|
|
88
50
|
|
|
89
51
|
@property
|
|
90
52
|
def client(self) -> S3Client:
|
|
91
53
|
"""Client Connection"""
|
|
92
54
|
if self.__client is None:
|
|
93
|
-
logger.info("Creating Client")
|
|
94
55
|
self.__client = self.session.client
|
|
95
56
|
|
|
96
|
-
if self.raise_on_error and self.__client is None:
|
|
97
|
-
raise RuntimeError("Client is not available")
|
|
98
57
|
return self.__client
|
|
99
58
|
|
|
100
59
|
@client.setter
|
|
@@ -317,3 +317,29 @@ class DatetimeUtility:
|
|
|
317
317
|
tz = pytz.timezone(timezone_name)
|
|
318
318
|
result = utc_datetime.astimezone(tz)
|
|
319
319
|
return result
|
|
320
|
+
|
|
321
|
+
@staticmethod
|
|
322
|
+
def get_timestamp(value: datetime | None | str) -> float:
|
|
323
|
+
"""Get a timestampe from a date or 0.0"""
|
|
324
|
+
if value is None:
|
|
325
|
+
return 0.0
|
|
326
|
+
if not isinstance(value, datetime):
|
|
327
|
+
value = DatetimeUtility.to_datetime_utc(value=value)
|
|
328
|
+
|
|
329
|
+
if not isinstance(value, datetime):
|
|
330
|
+
return 0.0
|
|
331
|
+
ts = value.timestamp()
|
|
332
|
+
return ts
|
|
333
|
+
|
|
334
|
+
@staticmethod
|
|
335
|
+
def get_timestamp_or_none(value: datetime | None | str) -> float | None:
|
|
336
|
+
"""Get a timestampe from a date or None"""
|
|
337
|
+
if value is None:
|
|
338
|
+
return None
|
|
339
|
+
if not isinstance(value, datetime):
|
|
340
|
+
value = DatetimeUtility.to_datetime_utc(value=value)
|
|
341
|
+
|
|
342
|
+
if not isinstance(value, datetime):
|
|
343
|
+
return None
|
|
344
|
+
ts = value.timestamp()
|
|
345
|
+
return ts
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DictionaryUtilitiy:
|
|
5
|
+
"""
|
|
6
|
+
A class to provide utility methods for working with dictionaries.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def find_dict_by_name(
|
|
11
|
+
dict_list: List[dict], key_field: str, name: str
|
|
12
|
+
) -> List[dict] | dict | str:
|
|
13
|
+
"""
|
|
14
|
+
Searches for dictionaries in a list where the key 'name' matches the specified value.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
dict_list (list): A list of dictionaries to search through.
|
|
18
|
+
key_field (str): The key to search for in each dictionary.
|
|
19
|
+
name (str): The value to search for in the 'key_field' key.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
list: A list of dictionaries where the 'key_field' key matches the specified value.
|
|
23
|
+
"""
|
|
24
|
+
# List comprehension to filter dictionaries that have the 'name' key equal to the specified name
|
|
25
|
+
|
|
26
|
+
return [d for d in dict_list if d.get(key_field) == name]
|