Skip to content

Commit 5510444

Browse files
Merging from v3
2 parents 98a0ccc + 26cfe7f commit 5510444

File tree

24 files changed

+555
-249
lines changed

24 files changed

+555
-249
lines changed

aws_lambda_powertools/utilities/data_classes/s3_object_event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class S3ObjectLambdaEvent(DictWrapper):
220220
import requests
221221
from aws_lambda_powertools.utilities.data_classes.s3_object_event import S3ObjectLambdaEvent
222222
223-
session = boto3.Session()
223+
session = boto3.session.Session()
224224
s3 = session.client("s3")
225225
226226
def lambda_handler(event, context):

aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
)
2424

2525
if TYPE_CHECKING:
26-
from mypy_boto3_dynamodb import DynamoDBClient
26+
from mypy_boto3_dynamodb.client import DynamoDBClient
2727
from mypy_boto3_dynamodb.type_defs import AttributeValueTypeDef
2828

2929
logger = logging.getLogger(__name__)
@@ -43,7 +43,7 @@ def __init__(
4343
validation_key_attr: str = "validation",
4444
boto_config: Optional[Config] = None,
4545
boto3_session: Optional[boto3.session.Session] = None,
46-
boto3_client: "DynamoDBClient" | None = None,
46+
boto3_client: Optional[DynamoDBClient] = None,
4747
):
4848
"""
4949
Initialize the DynamoDB client
@@ -71,7 +71,7 @@ def __init__(
7171
DynamoDB attribute name for hashed representation of the parts of the event used for validation
7272
boto_config: botocore.config.Config, optional
7373
Botocore configuration to pass during client initialization
74-
boto3_session : boto3.Session, optional
74+
boto3_session : boto3.session.Session, optional
7575
Boto3 session to use for AWS API communication
7676
boto3_client : DynamoDBClient, optional
7777
Boto3 DynamoDB Client to use, boto3_session and boto_config will be ignored if both are provided
@@ -91,11 +91,9 @@ def __init__(
9191
>>> return {"StatusCode": 200}
9292
"""
9393
if boto3_client is None:
94-
self._boto_config = boto_config or Config()
95-
self._boto3_session: boto3.Session = boto3_session or boto3.session.Session()
96-
self.client: "DynamoDBClient" = self._boto3_session.client("dynamodb", config=self._boto_config)
97-
else:
98-
self.client = boto3_client
94+
boto3_session = boto3_session or boto3.session.Session()
95+
boto3_client = boto3_session.client("dynamodb", config=boto_config)
96+
self.client = boto3_client
9997

10098
user_agent.register_feature_to_client(client=self.client, feature="idempotency")
10199

@@ -246,13 +244,20 @@ def _put_record(self, data_record: DataRecord) -> None:
246244
":now_in_millis": {"N": str(int(now.timestamp() * 1000))},
247245
":inprogress": {"S": STATUS_CONSTANTS["INPROGRESS"]},
248246
},
249-
**self.return_value_on_condition, # type: ignore
247+
**self.return_value_on_condition, # type: ignore[arg-type]
250248
)
251249
except ClientError as exc:
252250
error_code = exc.response.get("Error", {}).get("Code")
253251
if error_code == "ConditionalCheckFailedException":
254-
old_data_record = self._item_to_data_record(exc.response["Item"]) if "Item" in exc.response else None
255-
if old_data_record is not None:
252+
try:
253+
item = exc.response["Item"] # type: ignore[typeddict-item]
254+
except KeyError:
255+
logger.debug(
256+
f"Failed to put record for already existing idempotency key: {data_record.idempotency_key}",
257+
)
258+
raise IdempotencyItemAlreadyExistsError() from exc
259+
else:
260+
old_data_record = self._item_to_data_record(item)
256261
logger.debug(
257262
f"Failed to put record for already existing idempotency key: "
258263
f"{data_record.idempotency_key} with status: {old_data_record.status}, "
@@ -268,11 +273,6 @@ def _put_record(self, data_record: DataRecord) -> None:
268273

269274
raise IdempotencyItemAlreadyExistsError(old_data_record=old_data_record) from exc
270275

271-
logger.debug(
272-
f"Failed to put record for already existing idempotency key: {data_record.idempotency_key}",
273-
)
274-
raise IdempotencyItemAlreadyExistsError() from exc
275-
276276
raise
277277

278278
@staticmethod
@@ -297,7 +297,7 @@ def boto3_supports_condition_check_failure(boto3_version: str) -> bool:
297297
def _update_record(self, data_record: DataRecord):
298298
logger.debug(f"Updating record for idempotency key: {data_record.idempotency_key}")
299299
update_expression = "SET #response_data = :response_data, #expiry = :expiry, #status = :status"
300-
expression_attr_values: Dict[str, "AttributeValueTypeDef"] = {
300+
expression_attr_values: Dict[str, AttributeValueTypeDef] = {
301301
":expiry": {"N": str(data_record.expiry_timestamp)},
302302
":response_data": {"S": data_record.response_data},
303303
":status": {"S": data_record.status},

aws_lambda_powertools/utilities/parameters/appconfig.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
import warnings
7-
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
7+
from typing import TYPE_CHECKING, Dict, Optional, Union
88

99
import boto3
1010
from botocore.config import Config
@@ -13,7 +13,7 @@
1313
from aws_lambda_powertools.warnings import PowertoolsDeprecationWarning
1414

1515
if TYPE_CHECKING:
16-
from mypy_boto3_appconfigdata import AppConfigDataClient
16+
from mypy_boto3_appconfigdata.client import AppConfigDataClient
1717

1818
from aws_lambda_powertools.shared import constants
1919
from aws_lambda_powertools.shared.functions import (
@@ -69,8 +69,6 @@ class AppConfigProvider(BaseProvider):
6969
7070
"""
7171

72-
client: Any = None
73-
7472
def __init__(
7573
self,
7674
environment: str,
@@ -94,12 +92,11 @@ def __init__(
9492
stacklevel=2,
9593
)
9694

97-
self.client: "AppConfigDataClient" = self._build_boto3_client(
98-
service_name="appconfigdata",
99-
client=boto3_client,
100-
session=boto3_session,
101-
config=boto_config or config,
102-
)
95+
if boto3_client is None:
96+
boto3_session = boto3_session or boto3.session.Session()
97+
boto3_client = boto3_session.client("appconfigdata", config=boto_config or config)
98+
99+
self.client = boto3_client
103100

104101
self.application = resolve_env_var_choice(
105102
choice=application,
@@ -110,9 +107,11 @@ def __init__(
110107

111108
self._next_token: Dict[str, str] = {} # nosec - token for get_latest_configuration executions
112109
# Dict to store the recently retrieved value for a specific configuration.
113-
self.last_returned_value: Dict[str, str] = {}
110+
self.last_returned_value: Dict[str, bytes] = {}
111+
112+
super().__init__(client=self.client)
114113

115-
def _get(self, name: str, **sdk_options) -> str:
114+
def _get(self, name: str, **sdk_options) -> bytes:
116115
"""
117116
Retrieve a parameter value from AWS App config.
118117

aws_lambda_powertools/utilities/parameters/base.py

Lines changed: 5 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,30 @@
1010
from abc import ABC, abstractmethod
1111
from datetime import datetime, timedelta
1212
from typing import (
13-
TYPE_CHECKING,
1413
Any,
1514
Callable,
1615
Dict,
1716
NamedTuple,
1817
Optional,
1918
Tuple,
20-
Type,
2119
Union,
2220
cast,
2321
overload,
2422
)
2523

26-
import boto3
27-
from botocore.config import Config
28-
2924
from aws_lambda_powertools.shared import constants, user_agent
3025
from aws_lambda_powertools.shared.functions import resolve_max_age
3126
from aws_lambda_powertools.utilities.parameters.types import TransformOptions
3227

3328
from .exceptions import GetParameterError, TransformParameterError
3429

35-
if TYPE_CHECKING:
36-
from mypy_boto3_appconfigdata import AppConfigDataClient
37-
from mypy_boto3_dynamodb import DynamoDBServiceResource
38-
from mypy_boto3_secretsmanager import SecretsManagerClient
39-
from mypy_boto3_ssm import SSMClient
40-
41-
4230
DEFAULT_MAX_AGE_SECS = "300"
4331

4432
# These providers will be dynamically initialized on first use of the helper functions
4533
DEFAULT_PROVIDERS: Dict[str, Any] = {}
4634
TRANSFORM_METHOD_JSON = "json"
4735
TRANSFORM_METHOD_BINARY = "binary"
4836
SUPPORTED_TRANSFORM_METHODS = [TRANSFORM_METHOD_JSON, TRANSFORM_METHOD_BINARY]
49-
ParameterClients = Union["AppConfigDataClient", "SecretsManagerClient", "SSMClient"]
5037

5138
TRANSFORM_METHOD_MAPPING = {
5239
TRANSFORM_METHOD_JSON: json.loads,
@@ -69,10 +56,14 @@ class BaseProvider(ABC):
6956

7057
store: Dict[Tuple, ExpirableValue]
7158

72-
def __init__(self):
59+
def __init__(self, *, client=None, resource=None):
7360
"""
7461
Initialize the base provider
7562
"""
63+
if client is not None:
64+
user_agent.register_feature_to_client(client=client, feature="parameters")
65+
if resource is not None:
66+
user_agent.register_feature_to_resource(resource=resource, feature="parameters")
7667

7768
self.store: Dict[Tuple, ExpirableValue] = {}
7869

@@ -262,78 +253,6 @@ def _build_cache_key(
262253
"""
263254
return (name, transform, is_nested)
264255

265-
@staticmethod
266-
def _build_boto3_client(
267-
service_name: str,
268-
client: Optional[ParameterClients] = None,
269-
session: Optional[Type[boto3.Session]] = None,
270-
config: Optional[Type[Config]] = None,
271-
) -> Type[ParameterClients]:
272-
"""Builds a low level boto3 client with session and config provided
273-
274-
Parameters
275-
----------
276-
service_name : str
277-
AWS service name to instantiate a boto3 client, e.g. ssm
278-
client : Optional[ParameterClients], optional
279-
boto3 client instance, by default None
280-
session : Optional[Type[boto3.Session]], optional
281-
boto3 session instance, by default None
282-
config : Optional[Type[Config]], optional
283-
botocore config instance to configure client with, by default None
284-
285-
Returns
286-
-------
287-
Type[ParameterClients]
288-
Instance of a boto3 client for Parameters feature (e.g., ssm, appconfig, secretsmanager, etc.)
289-
"""
290-
if client is not None:
291-
user_agent.register_feature_to_client(client=client, feature="parameters")
292-
return client
293-
294-
session = session or boto3.Session()
295-
config = config or Config()
296-
client = session.client(service_name=service_name, config=config)
297-
user_agent.register_feature_to_client(client=client, feature="parameters")
298-
return client
299-
300-
# maintenance: change DynamoDBServiceResource type to ParameterResourceClients when we expand
301-
@staticmethod
302-
def _build_boto3_resource_client(
303-
service_name: str,
304-
client: Optional["DynamoDBServiceResource"] = None,
305-
session: Optional[Type[boto3.Session]] = None,
306-
config: Optional[Type[Config]] = None,
307-
endpoint_url: Optional[str] = None,
308-
) -> "DynamoDBServiceResource":
309-
"""Builds a high level boto3 resource client with session, config and endpoint_url provided
310-
311-
Parameters
312-
----------
313-
service_name : str
314-
AWS service name to instantiate a boto3 client, e.g. ssm
315-
client : Optional[DynamoDBServiceResource], optional
316-
boto3 client instance, by default None
317-
session : Optional[Type[boto3.Session]], optional
318-
boto3 session instance, by default None
319-
config : Optional[Type[Config]], optional
320-
botocore config instance to configure client, by default None
321-
322-
Returns
323-
-------
324-
Type[DynamoDBServiceResource]
325-
Instance of a boto3 resource client for Parameters feature (e.g., dynamodb, etc.)
326-
"""
327-
if client is not None:
328-
user_agent.register_feature_to_resource(resource=client, feature="parameters")
329-
return client
330-
331-
session = session or boto3.Session()
332-
config = config or Config()
333-
client = session.resource(service_name=service_name, config=config, endpoint_url=endpoint_url)
334-
user_agent.register_feature_to_resource(resource=client, feature="parameters")
335-
return client
336-
337256

338257
def get_transform_method(value: str, transform: TransformOptions = None) -> Callable[..., Any]:
339258
"""

aws_lambda_powertools/utilities/parameters/dynamodb.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
from .base import BaseProvider
1515

1616
if TYPE_CHECKING:
17-
from mypy_boto3_dynamodb import DynamoDBServiceResource
18-
from mypy_boto3_dynamodb.service_resource import Table
17+
from mypy_boto3_dynamodb.service_resource import DynamoDBServiceResource
1918

2019

2120
class DynamoDBProvider(BaseProvider):
@@ -174,19 +173,16 @@ def __init__(
174173
stacklevel=2,
175174
)
176175

177-
self.table: "Table" = self._build_boto3_resource_client(
178-
service_name="dynamodb",
179-
client=boto3_client,
180-
session=boto3_session,
181-
config=boto_config or config,
182-
endpoint_url=endpoint_url,
183-
).Table(table_name)
176+
if boto3_client is None:
177+
boto3_session = boto3_session or boto3.session.Session()
178+
boto3_client = boto3_session.resource("dynamodb", config=boto_config or config, endpoint_url=endpoint_url)
184179

180+
self.table = boto3_client.Table(table_name)
185181
self.key_attr = key_attr
186182
self.sort_attr = sort_attr
187183
self.value_attr = value_attr
188184

189-
super().__init__()
185+
super().__init__(resource=boto3_client)
190186

191187
def _get(self, name: str, **sdk_options) -> str:
192188
"""

0 commit comments

Comments
 (0)