diff --git a/.github/workflows/secure_workflows.yml b/.github/workflows/secure_workflows.yml
index cfb0a51a9a1..e4d79a1ae5b 100644
--- a/.github/workflows/secure_workflows.yml
+++ b/.github/workflows/secure_workflows.yml
@@ -16,7 +16,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: Ensure 3rd party workflows have SHA pinned
- uses: zgosalvez/github-actions-ensure-sha-pinned-actions@bd2868d14a756969608c618665394415a238de69 # v2.0.5
+ uses: zgosalvez/github-actions-ensure-sha-pinned-actions@b9ddf6a5153efe6fb94f071c8915175afdce60fa # v2.1.0
with:
# Trusted GitHub Actions and/or organizations
allowlist: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 27ca3031ed5..c1ae5156b7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,39 @@
## Bug Fixes
+* **ci:** upgraded cdk to match the version used on e2e tests
+* **feature-flags:** revert RuleAction Enum inheritance on str ([#1910](https://github.com/awslabs/aws-lambda-powertools-python/issues/1910))
+* **logger:** support exception and exception_name fields at any log level ([#1930](https://github.com/awslabs/aws-lambda-powertools-python/issues/1930))
+* **metrics:** clarify no-metrics user warning ([#1935](https://github.com/awslabs/aws-lambda-powertools-python/issues/1935))
+
+## Documentation
+
+* **event_handlers:** Fix REST API - HTTP Methods documentation ([#1936](https://github.com/awslabs/aws-lambda-powertools-python/issues/1936))
+* **home:** update powertools definition
+* **we-made-this:** add CI/CD using Feature Flags video ([#1940](https://github.com/awslabs/aws-lambda-powertools-python/issues/1940))
+* **we-made-this:** add Feature Flags post ([#1939](https://github.com/awslabs/aws-lambda-powertools-python/issues/1939))
+
+## Features
+
+* **batch:** add support to SQS FIFO queues (SqsFifoPartialProcessor) ([#1934](https://github.com/awslabs/aws-lambda-powertools-python/issues/1934))
+
+## Maintenance
+
+* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.0.5 to 2.1.0 ([#1943](https://github.com/awslabs/aws-lambda-powertools-python/issues/1943))
+* **deps:** bump pydantic from 1.10.4 to 1.10.5 ([#1931](https://github.com/awslabs/aws-lambda-powertools-python/issues/1931))
+* **deps-dev:** bump mkdocs-material from 9.0.12 to 9.0.13 ([#1944](https://github.com/awslabs/aws-lambda-powertools-python/issues/1944))
+* **deps-dev:** bump aws-cdk-lib from 2.64.0 to 2.65.0 ([#1938](https://github.com/awslabs/aws-lambda-powertools-python/issues/1938))
+* **deps-dev:** bump types-python-dateutil from 2.8.19.6 to 2.8.19.7 ([#1932](https://github.com/awslabs/aws-lambda-powertools-python/issues/1932))
+* **deps-dev:** bump types-requests from 2.28.11.12 to 2.28.11.13 ([#1933](https://github.com/awslabs/aws-lambda-powertools-python/issues/1933))
+* **deps-dev:** bump mypy-boto3-appconfig from 1.26.63 to 1.26.71 ([#1928](https://github.com/awslabs/aws-lambda-powertools-python/issues/1928))
+* **deps-dev:** bump flake8-bugbear from 23.1.20 to 23.2.13 ([#1924](https://github.com/awslabs/aws-lambda-powertools-python/issues/1924))
+* **deps-dev:** bump mypy-boto3-appconfigdata from 1.26.0.post1 to 1.26.70 ([#1925](https://github.com/awslabs/aws-lambda-powertools-python/issues/1925))
+
+
+
+## [v2.8.0] - 2023-02-10
+## Bug Fixes
+
* **idempotency:** make idempotent_function decorator thread safe ([#1899](https://github.com/awslabs/aws-lambda-powertools-python/issues/1899))
## Documentation
@@ -24,13 +57,14 @@
## Maintenance
+* update v2 layer ARN on documentation
* **deps:** bump docker/setup-buildx-action from 2.4.0 to 2.4.1 ([#1903](https://github.com/awslabs/aws-lambda-powertools-python/issues/1903))
-* **deps-dev:** bump mkdocs-material from 9.0.11 to 9.0.12 ([#1919](https://github.com/awslabs/aws-lambda-powertools-python/issues/1919))
+* **deps-dev:** bump aws-cdk-lib from 2.63.0 to 2.63.2 ([#1904](https://github.com/awslabs/aws-lambda-powertools-python/issues/1904))
* **deps-dev:** bump black from 22.12.0 to 23.1.0 ([#1886](https://github.com/awslabs/aws-lambda-powertools-python/issues/1886))
* **deps-dev:** bump types-requests from 2.28.11.8 to 2.28.11.12 ([#1906](https://github.com/awslabs/aws-lambda-powertools-python/issues/1906))
* **deps-dev:** bump pytest-xdist from 3.1.0 to 3.2.0 ([#1905](https://github.com/awslabs/aws-lambda-powertools-python/issues/1905))
* **deps-dev:** bump aws-cdk-lib from 2.63.2 to 2.64.0 ([#1918](https://github.com/awslabs/aws-lambda-powertools-python/issues/1918))
-* **deps-dev:** bump aws-cdk-lib from 2.63.0 to 2.63.2 ([#1904](https://github.com/awslabs/aws-lambda-powertools-python/issues/1904))
+* **deps-dev:** bump mkdocs-material from 9.0.11 to 9.0.12 ([#1919](https://github.com/awslabs/aws-lambda-powertools-python/issues/1919))
* **deps-dev:** bump mkdocs-material from 9.0.10 to 9.0.11 ([#1896](https://github.com/awslabs/aws-lambda-powertools-python/issues/1896))
* **deps-dev:** bump mypy-boto3-appconfig from 1.26.0.post1 to 1.26.63 ([#1895](https://github.com/awslabs/aws-lambda-powertools-python/issues/1895))
* **deps-dev:** bump mypy-boto3-s3 from 1.26.58 to 1.26.62 ([#1889](https://github.com/awslabs/aws-lambda-powertools-python/issues/1889))
@@ -2857,7 +2891,8 @@
* Merge pull request [#5](https://github.com/awslabs/aws-lambda-powertools-python/issues/5) from jfuss/feat/python38
-[Unreleased]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.7.1...HEAD
+[Unreleased]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.8.0...HEAD
+[v2.8.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.7.1...v2.8.0
[v2.7.1]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.7.0...v2.7.1
[v2.7.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.6.0...v2.7.0
[v2.6.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.5.0...v2.6.0
diff --git a/README.md b/README.md
index 49d53747a83..cb0aa00d83b 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[](https://app.codecov.io/gh/awslabs/aws-lambda-powertools-python)
   [](https://discord.gg/B8zZKbbyET)
-A suite of Python utilities for AWS Lambda functions to ease adopting best practices such as tracing, structured logging, custom metrics, [and more](https://awslabs.github.io/aws-lambda-powertools-python/latest/#features).
+Powertools is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-python/latest/#features).
> Also available in [Java](https://github.com/awslabs/aws-lambda-powertools-java), [Typescript](https://github.com/awslabs/aws-lambda-powertools-typescript), and [.NET](https://awslabs.github.io/aws-lambda-powertools-dotnet/).
diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py
index a44d85455fe..78993f92c5e 100644
--- a/aws_lambda_powertools/event_handler/api_gateway.py
+++ b/aws_lambda_powertools/event_handler/api_gateway.py
@@ -501,7 +501,9 @@ def register_resolver(func: Callable):
self._routes.append(Route(item, self._compile_regex(rule), func, cors_enabled, compress, cache_control))
route_key = item + rule
if route_key in self._route_keys:
- warnings.warn(f"A route like this was already registered. method: '{item}' rule: '{rule}'")
+ warnings.warn(
+ f"A route like this was already registered. method: '{item}' rule: '{rule}'", stacklevel=2
+ )
self._route_keys.append(route_key)
if cors_enabled:
logger.debug(f"Registering method {item.upper()} to Allow Methods in CORS")
@@ -526,7 +528,9 @@ def resolve(self, event, context) -> Dict[str, Any]:
"""
if isinstance(event, BaseProxyEvent):
warnings.warn(
- "You don't need to serialize event to Event Source Data Class when using Event Handler; see issue #1152"
+ "You don't need to serialize event to Event Source Data Class when using Event Handler; "
+ "see issue #1152",
+ stacklevel=2,
)
event = event.raw_event
diff --git a/aws_lambda_powertools/logging/formatter.py b/aws_lambda_powertools/logging/formatter.py
index 07971ba0a26..0d0750b4085 100644
--- a/aws_lambda_powertools/logging/formatter.py
+++ b/aws_lambda_powertools/logging/formatter.py
@@ -152,7 +152,11 @@ def format(self, record: logging.LogRecord) -> str: # noqa: A003
"""Format logging record as structured JSON str"""
formatted_log = self._extract_log_keys(log_record=record)
formatted_log["message"] = self._extract_log_message(log_record=record)
- formatted_log["exception"], formatted_log["exception_name"] = self._extract_log_exception(log_record=record)
+ # exception and exception_name fields can be added as extra key
+ # in any log level, we try to extract and use them first
+ extracted_exception, extracted_exception_name = self._extract_log_exception(log_record=record)
+ formatted_log["exception"] = formatted_log.get("exception", extracted_exception)
+ formatted_log["exception_name"] = formatted_log.get("exception_name", extracted_exception_name)
formatted_log["xray_trace_id"] = self._get_latest_trace_id()
formatted_log = self._strip_none_records(records=formatted_log)
diff --git a/aws_lambda_powertools/metrics/base.py b/aws_lambda_powertools/metrics/base.py
index 67dcb47c282..b96356192ab 100644
--- a/aws_lambda_powertools/metrics/base.py
+++ b/aws_lambda_powertools/metrics/base.py
@@ -391,7 +391,11 @@ def decorate(event, context):
self._add_cold_start_metric(context=context)
finally:
if not raise_on_empty_metrics and not self.metric_set:
- warnings.warn("No metrics to publish, skipping")
+ warnings.warn(
+ "No application metrics to publish. The cold-start metric may be published if enabled. "
+ "If application metrics should never be empty, consider using 'raise_on_empty_metrics'",
+ stacklevel=2,
+ )
else:
metrics = self.serialize_metric_set()
self.clear_metrics()
diff --git a/aws_lambda_powertools/shared/functions.py b/aws_lambda_powertools/shared/functions.py
index b1c53d989bb..86ba74d5e78 100644
--- a/aws_lambda_powertools/shared/functions.py
+++ b/aws_lambda_powertools/shared/functions.py
@@ -106,7 +106,9 @@ def bytes_to_string(value: bytes) -> str:
def powertools_dev_is_set() -> bool:
is_on = strtobool(os.getenv(constants.POWERTOOLS_DEV_ENV, "0"))
if is_on:
- warnings.warn("POWERTOOLS_DEV environment variable is enabled. Increasing verbosity across utilities.")
+ warnings.warn(
+ "POWERTOOLS_DEV environment variable is enabled. Increasing verbosity across utilities.", stacklevel=2
+ )
return True
return False
@@ -115,7 +117,7 @@ def powertools_dev_is_set() -> bool:
def powertools_debug_is_set() -> bool:
is_on = strtobool(os.getenv(constants.POWERTOOLS_DEBUG_ENV, "0"))
if is_on:
- warnings.warn("POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG.")
+ warnings.warn("POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG.", stacklevel=2)
return True
return False
diff --git a/aws_lambda_powertools/shared/headers_serializer.py b/aws_lambda_powertools/shared/headers_serializer.py
index b4e9f7c7c5f..aa38157e26f 100644
--- a/aws_lambda_powertools/shared/headers_serializer.py
+++ b/aws_lambda_powertools/shared/headers_serializer.py
@@ -97,7 +97,8 @@ def serialize(self, headers: Dict[str, Union[str, List[str]]], cookies: List[Coo
if len(cookies) > 1:
warnings.warn(
"Can't encode more than one cookie in the response. Sending the last cookie only. "
- "Did you enable multiValueHeaders on the ALB Target Group?"
+ "Did you enable multiValueHeaders on the ALB Target Group?",
+ stacklevel=2,
)
# We can only send one cookie, send the last one
@@ -114,7 +115,8 @@ def serialize(self, headers: Dict[str, Union[str, List[str]]], cookies: List[Coo
if len(values) > 1:
warnings.warn(
f"Can't encode more than one header value for the same key ('{key}') in the response. "
- "Did you enable multiValueHeaders on the ALB Target Group?"
+ "Did you enable multiValueHeaders on the ALB Target Group?",
+ stacklevel=2,
)
# We can only set one header per key, send the last one
diff --git a/aws_lambda_powertools/utilities/batch/__init__.py b/aws_lambda_powertools/utilities/batch/__init__.py
index 02f3e786441..0e2637cc358 100644
--- a/aws_lambda_powertools/utilities/batch/__init__.py
+++ b/aws_lambda_powertools/utilities/batch/__init__.py
@@ -16,16 +16,22 @@
batch_processor,
)
from aws_lambda_powertools.utilities.batch.exceptions import ExceptionInfo
+from aws_lambda_powertools.utilities.batch.sqs_fifo_partial_processor import (
+ SqsFifoPartialProcessor,
+)
+from aws_lambda_powertools.utilities.batch.types import BatchTypeModels
__all__ = (
"BatchProcessor",
"AsyncBatchProcessor",
"BasePartialProcessor",
"BasePartialBatchProcessor",
+ "BatchTypeModels",
"ExceptionInfo",
"EventType",
"FailureResponse",
"SuccessResponse",
+ "SqsFifoPartialProcessor",
"batch_processor",
"async_batch_processor",
)
diff --git a/aws_lambda_powertools/utilities/batch/base.py b/aws_lambda_powertools/utilities/batch/base.py
index 171858c6d11..3aea2b70fa4 100644
--- a/aws_lambda_powertools/utilities/batch/base.py
+++ b/aws_lambda_powertools/utilities/batch/base.py
@@ -19,7 +19,6 @@
List,
Optional,
Tuple,
- Type,
Union,
overload,
)
@@ -30,6 +29,7 @@
BatchProcessingError,
ExceptionInfo,
)
+from aws_lambda_powertools.utilities.batch.types import BatchTypeModels
from aws_lambda_powertools.utilities.data_classes.dynamo_db_stream_event import (
DynamoDBRecord,
)
@@ -48,24 +48,6 @@ class EventType(Enum):
DynamoDBStreams = "DynamoDBStreams"
-#
-# type specifics
-#
-has_pydantic = "pydantic" in sys.modules
-
-# For IntelliSense and Mypy to work, we need to account for possible SQS, Kinesis and DynamoDB subclasses
-# We need them as subclasses as we must access their message ID or sequence number metadata via dot notation
-if has_pydantic:
- from aws_lambda_powertools.utilities.parser.models import DynamoDBStreamRecordModel
- from aws_lambda_powertools.utilities.parser.models import (
- KinesisDataStreamRecord as KinesisDataStreamRecordModel,
- )
- from aws_lambda_powertools.utilities.parser.models import SqsRecordModel
-
- BatchTypeModels = Optional[
- Union[Type[SqsRecordModel], Type[DynamoDBStreamRecordModel], Type[KinesisDataStreamRecordModel]]
- ]
-
# When using processor with default arguments, records will carry EventSourceDataClassTypes
# and depending on what EventType it's passed it'll correctly map to the right record
# When using Pydantic Models, it'll accept any subclass from SQS, DynamoDB and Kinesis
diff --git a/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py b/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py
new file mode 100644
index 00000000000..d48749a137e
--- /dev/null
+++ b/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py
@@ -0,0 +1,92 @@
+from typing import List, Optional, Tuple
+
+from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType
+from aws_lambda_powertools.utilities.batch.types import BatchSqsTypeModel
+
+
+class SQSFifoCircuitBreakerError(Exception):
+ """
+ Signals a record not processed due to the SQS FIFO processing being interrupted
+ """
+
+ pass
+
+
+class SqsFifoPartialProcessor(BatchProcessor):
+ """Process native partial responses from SQS FIFO queues.
+
+ Stops processing records when the first record fails. The remaining records are reported as failed items.
+
+ Example
+ _______
+
+ ## Process batch triggered by a FIFO SQS
+
+ ```python
+ import json
+
+ from aws_lambda_powertools import Logger, Tracer
+ from aws_lambda_powertools.utilities.batch import SqsFifoPartialProcessor, EventType, batch_processor
+ from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
+ from aws_lambda_powertools.utilities.typing import LambdaContext
+
+
+ processor = SqsFifoPartialProcessor()
+ tracer = Tracer()
+ logger = Logger()
+
+
+ @tracer.capture_method
+ def record_handler(record: SQSRecord):
+ payload: str = record.body
+ if payload:
+ item: dict = json.loads(payload)
+ ...
+
+ @logger.inject_lambda_context
+ @tracer.capture_lambda_handler
+ @batch_processor(record_handler=record_handler, processor=processor)
+ def lambda_handler(event, context: LambdaContext):
+ return processor.response()
+ ```
+ """
+
+ circuit_breaker_exc = (
+ SQSFifoCircuitBreakerError,
+ SQSFifoCircuitBreakerError("A previous record failed processing"),
+ None,
+ )
+
+ def __init__(self, model: Optional["BatchSqsTypeModel"] = None):
+ super().__init__(EventType.SQS, model)
+
+ def process(self) -> List[Tuple]:
+ """
+ Call instance's handler for each record. When the first failed message is detected,
+ the process is short-circuited, and the remaining messages are reported as failed items.
+ """
+ result: List[Tuple] = []
+
+ for i, record in enumerate(self.records):
+ # If we have failed messages, it means that the last message failed.
+ # We then short circuit the process, failing the remaining messages
+ if self.fail_messages:
+ return self._short_circuit_processing(i, result)
+
+ # Otherwise, process the message normally
+ result.append(self._process_record(record))
+
+ return result
+
+ def _short_circuit_processing(self, first_failure_index: int, result: List[Tuple]) -> List[Tuple]:
+ """
+ Starting from the first failure index, fail all the remaining messages, and append them to the result list.
+ """
+ remaining_records = self.records[first_failure_index:]
+ for remaining_record in remaining_records:
+ data = self._to_batch_type(record=remaining_record, event_type=self.event_type, model=self.model)
+ result.append(self.failure_handler(record=data, exception=self.circuit_breaker_exc))
+ return result
+
+ async def _async_process_record(self, record: dict):
+ raise NotImplementedError()
diff --git a/aws_lambda_powertools/utilities/batch/types.py b/aws_lambda_powertools/utilities/batch/types.py
new file mode 100644
index 00000000000..1fc5aba4fc4
--- /dev/null
+++ b/aws_lambda_powertools/utilities/batch/types.py
@@ -0,0 +1,24 @@
+#
+# type specifics
+#
+import sys
+from typing import Optional, Type, Union
+
+has_pydantic = "pydantic" in sys.modules
+
+# For IntelliSense and Mypy to work, we need to account for possible SQS subclasses
+# We need them as subclasses as we must access their message ID or sequence number metadata via dot notation
+if has_pydantic:
+ from aws_lambda_powertools.utilities.parser.models import DynamoDBStreamRecordModel
+ from aws_lambda_powertools.utilities.parser.models import (
+ KinesisDataStreamRecord as KinesisDataStreamRecordModel,
+ )
+ from aws_lambda_powertools.utilities.parser.models import SqsRecordModel
+
+ BatchTypeModels = Optional[
+ Union[Type[SqsRecordModel], Type[DynamoDBStreamRecordModel], Type[KinesisDataStreamRecordModel]]
+ ]
+ BatchSqsTypeModel = Optional[Type[SqsRecordModel]]
+else:
+ BatchTypeModels = "BatchTypeModels" # type: ignore
+ BatchSqsTypeModel = "BatchSqsTypeModel" # type: ignore
diff --git a/aws_lambda_powertools/utilities/feature_flags/schema.py b/aws_lambda_powertools/utilities/feature_flags/schema.py
index 2fb690301c6..b7df5f5fa4f 100644
--- a/aws_lambda_powertools/utilities/feature_flags/schema.py
+++ b/aws_lambda_powertools/utilities/feature_flags/schema.py
@@ -23,7 +23,7 @@
HOUR_MIN_SEPARATOR = ":"
-class RuleAction(Enum):
+class RuleAction(str, Enum):
EQUALS = "EQUALS"
NOT_EQUALS = "NOT_EQUALS"
KEY_GREATER_THAN_VALUE = "KEY_GREATER_THAN_VALUE"
diff --git a/aws_lambda_powertools/utilities/idempotency/persistence/base.py b/aws_lambda_powertools/utilities/idempotency/persistence/base.py
index b3504dfeacd..28b284b8e5e 100644
--- a/aws_lambda_powertools/utilities/idempotency/persistence/base.py
+++ b/aws_lambda_powertools/utilities/idempotency/persistence/base.py
@@ -182,7 +182,7 @@ def _get_hashed_idempotency_key(self, data: Dict[str, Any]) -> str:
if self.is_missing_idempotency_key(data=data):
if self.raise_on_no_idempotency_key:
raise IdempotencyKeyError("No data found to create a hashed idempotency_key")
- warnings.warn(f"No value found for idempotency_key. jmespath: {self.event_key_jmespath}")
+ warnings.warn(f"No value found for idempotency_key. jmespath: {self.event_key_jmespath}", stacklevel=2)
generated_hash = self._generate_hash(data=data)
return f"{self.function_name}#{generated_hash}"
@@ -359,7 +359,8 @@ def save_inprogress(self, data: Dict[str, Any], remaining_time_in_millis: Option
else:
warnings.warn(
"Couldn't determine the remaining time left. "
- "Did you call register_lambda_context on IdempotencyConfig?"
+ "Did you call register_lambda_context on IdempotencyConfig?",
+ stacklevel=2,
)
logger.debug(f"Saving in progress record for idempotency key: {data_record.idempotency_key}")
diff --git a/docs/core/event_handler/api_gateway.md b/docs/core/event_handler/api_gateway.md
index 802b6112e04..9348575535a 100644
--- a/docs/core/event_handler/api_gateway.md
+++ b/docs/core/event_handler/api_gateway.md
@@ -166,7 +166,7 @@ You can also combine nested paths with greedy regex to catch in between routes.
### HTTP Methods
-You can use named decorators to specify the HTTP method that should be handled in your functions. That is, `app.`, where the HTTP method could be `get`, `post`, `put`, `patch`, `delete`, and `options`.
+You can use named decorators to specify the HTTP method that should be handled in your functions. That is, `app.`, where the HTTP method could be `get`, `post`, `put`, `patch` and `delete`.
=== "http_methods.py"
diff --git a/docs/index.md b/docs/index.md
index ab7bedbda66..d01208267d5 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -26,8 +26,8 @@ A suite of utilities for AWS Lambda functions to ease adopting best practices su
Powertools is available in the following formats:
-* **Lambda Layer (x86_64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21**](#){: .copyMe}:clipboard:
-* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21**](#){: .copyMe}:clipboard:
+* **Lambda Layer (x86_64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22**](#){: .copyMe}:clipboard:
+* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22**](#){: .copyMe}:clipboard:
* **PyPi**: **`pip install "aws-lambda-powertools"`**
???+ info "Some utilities require additional dependencies"
@@ -67,55 +67,55 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
| Region | Layer ARN |
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
- | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:21](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:22](#){: .copyMe}:clipboard: |
=== "arm64"
| Region | Layer ARN |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
- | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22](#){: .copyMe}:clipboard: |
??? note "Note: Click to expand and copy code snippets for popular frameworks"
@@ -128,7 +128,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
Type: AWS::Serverless::Function
Properties:
Layers:
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22
```
=== "Serverless framework"
@@ -138,7 +138,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
hello:
handler: lambda_function.lambda_handler
layers:
- - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22
```
=== "CDK"
@@ -154,7 +154,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
powertools_layer = aws_lambda.LayerVersion.from_layer_version_arn(
self,
id="lambda-powertools",
- layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22"
)
aws_lambda.Function(self,
'sample-app-lambda',
@@ -203,7 +203,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "python3.9"
- layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22"]
source_code_hash = filebase64sha256("lambda_function_payload.zip")
}
@@ -256,7 +256,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
? Do you want to configure advanced settings? Yes
...
? Do you want to enable Lambda layers for this function? Yes
- ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22
❯ amplify push -y
@@ -267,7 +267,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
- Name:
? Which setting do you want to update? Lambda layers configuration
? Do you want to enable Lambda layers for this function? Yes
- ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:21
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:22
? Do you want to edit the local lambda function now? No
```
@@ -276,7 +276,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
Change {region} to your AWS region, e.g. `eu-west-1`
```bash title="AWS CLI"
- aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:21 --region {region}
+ aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:22 --region {region}
```
The pre-signed URL to download this Lambda Layer will be within `Location` key.
@@ -291,7 +291,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
Properties:
Architectures: [arm64]
Layers:
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22
```
=== "Serverless framework"
@@ -302,7 +302,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
handler: lambda_function.lambda_handler
architecture: arm64
layers:
- - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22
```
=== "CDK"
@@ -318,7 +318,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
powertools_layer = aws_lambda.LayerVersion.from_layer_version_arn(
self,
id="lambda-powertools",
- layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22"
)
aws_lambda.Function(self,
'sample-app-lambda',
@@ -368,7 +368,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "python3.9"
- layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22"]
architectures = ["arm64"]
source_code_hash = filebase64sha256("lambda_function_payload.zip")
@@ -424,7 +424,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
? Do you want to configure advanced settings? Yes
...
? Do you want to enable Lambda layers for this function? Yes
- ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22
❯ amplify push -y
@@ -435,7 +435,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
- Name:
? Which setting do you want to update? Lambda layers configuration
? Do you want to enable Lambda layers for this function? Yes
- ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22
? Do you want to edit the local lambda function now? No
```
@@ -443,7 +443,7 @@ You can include Powertools Lambda Layer using [AWS Lambda Console](https://docs.
Change {region} to your AWS region, e.g. `eu-west-1`
```bash title="AWS CLI"
- aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:21 --region {region}
+ aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:22 --region {region}
```
The pre-signed URL to download this Lambda Layer will be within `Location` key.
diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md
index 4a53e053f44..0f899673c2e 100644
--- a/docs/utilities/batch.md
+++ b/docs/utilities/batch.md
@@ -347,6 +347,23 @@ Processing batches from SQS works in four stages:
}
```
+#### FIFO queues
+
+When using [SQS FIFO queues](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html){target="_blank"}, we will stop processing messages after the first failure, and return all failed and unprocessed messages in `batchItemFailures`.
+This helps preserve the ordering of messages in your queue.
+
+=== "As a decorator"
+
+ ```python hl_lines="5 11"
+ --8<-- "examples/batch_processing/src/sqs_fifo_batch_processor.py"
+ ```
+
+=== "As a context manager"
+
+ ```python hl_lines="4 8"
+ --8<-- "examples/batch_processing/src/sqs_fifo_batch_processor_context_manager.py"
+ ```
+
### Processing messages from Kinesis
Processing batches from Kinesis works in four stages:
diff --git a/docs/we_made_this.md b/docs/we_made_this.md
index 1aa52df5cce..950a8c9f24d 100644
--- a/docs/we_made_this.md
+++ b/docs/we_made_this.md
@@ -68,6 +68,14 @@ This article walks through a sample AWS EventBridge cookiecutter template presen
* [binx.io/2022/10/11/speedup-event-driven-projects/](https://binx.io/2022/10/11/speedup-event-driven-projects/){target="_blank"}
* [Slides](https://www.slideshare.net/JorisConijn/let-codecommit-work-for-you){target="_blank"}
+### Implementing Feature Flags with AWS AppConfig and AWS Lambda Powertools
+
+> **Author: [Ran Isenberg](mailto:ran.isenberg@ranthebuilder.cloud) [:material-twitter:](https://twitter.com/IsenbergRan){target="_blank"} [:material-linkedin:](https://www.linkedin.com/in/ranisenberg/){target="_blank"}**
+
+This article walks through how CyberArk uses Powertools to implement Feature Flags with AWS AppConfig
+
+* [aws.amazon.com/blogs/mt/how-cyberark-implements-feature-flags-with-aws-appconfig](https://aws.amazon.com/blogs/mt/how-cyberark-implements-feature-flags-with-aws-appconfig){target="_blank"}
+
## Videos
#### Building a resilient input handling with Parser
@@ -88,6 +96,16 @@ A deep dive in the [Feature Flags](./utilities/feature_flags.md){target="_blank"
+#### Level Up Your CI/CD With Smart AWS Feature Flags
+
+> **Author: [Ran Isenberg](mailto:ran.isenberg@ranthebuilder.cloud) [:material-twitter:](https://twitter.com/IsenbergRan){target="_blank"} [:material-linkedin:](https://www.linkedin.com/in/ranisenberg/){target="_blank"}**
+
+Feature flags can improve your CI/CD process by enabling capabilities otherwise not possible, thus making them an enabler of DevOps and a crucial part of continuous integration. Partial rollouts, A/B testing, and the ability to quickly change a configuration without redeploying code are advantages you gain by using features flags.
+
+In this talk, you will learn the added value of using feature flags as part of your CI/CD process and how AWS Lambda Powertools can help with that.
+
+
+
## Workshops
### Introduction to Lambda Powertools
diff --git a/examples/batch_processing/src/sqs_fifo_batch_processor.py b/examples/batch_processing/src/sqs_fifo_batch_processor.py
new file mode 100644
index 00000000000..a5fe9f23235
--- /dev/null
+++ b/examples/batch_processing/src/sqs_fifo_batch_processor.py
@@ -0,0 +1,23 @@
+from aws_lambda_powertools import Logger, Tracer
+from aws_lambda_powertools.utilities.batch import (
+ SqsFifoPartialProcessor,
+ batch_processor,
+)
+from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
+from aws_lambda_powertools.utilities.typing import LambdaContext
+
+processor = SqsFifoPartialProcessor()
+tracer = Tracer()
+logger = Logger()
+
+
+@tracer.capture_method
+def record_handler(record: SQSRecord):
+ ...
+
+
+@logger.inject_lambda_context
+@tracer.capture_lambda_handler
+@batch_processor(record_handler=record_handler, processor=processor)
+def lambda_handler(event, context: LambdaContext):
+ return processor.response()
diff --git a/examples/batch_processing/src/sqs_fifo_batch_processor_context_manager.py b/examples/batch_processing/src/sqs_fifo_batch_processor_context_manager.py
new file mode 100644
index 00000000000..45759b2a585
--- /dev/null
+++ b/examples/batch_processing/src/sqs_fifo_batch_processor_context_manager.py
@@ -0,0 +1,23 @@
+from aws_lambda_powertools import Logger, Tracer
+from aws_lambda_powertools.utilities.batch import SqsFifoPartialProcessor
+from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
+from aws_lambda_powertools.utilities.typing import LambdaContext
+
+processor = SqsFifoPartialProcessor()
+tracer = Tracer()
+logger = Logger()
+
+
+@tracer.capture_method
+def record_handler(record: SQSRecord):
+ ...
+
+
+@logger.inject_lambda_context
+@tracer.capture_lambda_handler
+def lambda_handler(event, context: LambdaContext):
+ batch = event["Records"]
+ with processor(records=batch, handler=record_handler):
+ processor.process() # kick off processing, return List[Tuple]
+
+ return processor.response()
diff --git a/package-lock.json b/package-lock.json
index f466bfd38a1..0b1629193a8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,13 +8,13 @@
"name": "aws-lambda-powertools-python-e2e",
"version": "1.0.0",
"devDependencies": {
- "aws-cdk": "2.61.1"
+ "aws-cdk": "^2.65.0"
}
},
"node_modules/aws-cdk": {
- "version": "2.61.1",
- "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.61.1.tgz",
- "integrity": "sha512-bufvOB+I2T6YjVjT6D0j6xbi6CnX62j7TfHL905WLd5UQhPecwlL8wLe2whUOKqZj2wmGjaP7jAjIE1FFfh98w==",
+ "version": "2.65.0",
+ "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.65.0.tgz",
+ "integrity": "sha512-a1xtdf95N7MLPcwCs+IkE6kCD1utLMuDh+1RIyYbX7SYzAjlNJzOXQ7M16kBktt4KflVWseZnYsGbKWJ2DraMw==",
"dev": true,
"bin": {
"cdk": "bin/cdk"
@@ -43,9 +43,9 @@
},
"dependencies": {
"aws-cdk": {
- "version": "2.61.1",
- "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.61.1.tgz",
- "integrity": "sha512-bufvOB+I2T6YjVjT6D0j6xbi6CnX62j7TfHL905WLd5UQhPecwlL8wLe2whUOKqZj2wmGjaP7jAjIE1FFfh98w==",
+ "version": "2.65.0",
+ "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.65.0.tgz",
+ "integrity": "sha512-a1xtdf95N7MLPcwCs+IkE6kCD1utLMuDh+1RIyYbX7SYzAjlNJzOXQ7M16kBktt4KflVWseZnYsGbKWJ2DraMw==",
"dev": true,
"requires": {
"fsevents": "2.3.2"
diff --git a/package.json b/package.json
index 1d9a62c13d9..d8fcb108a1e 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,6 @@
"name": "aws-lambda-powertools-python-e2e",
"version": "1.0.0",
"devDependencies": {
- "aws-cdk": "2.61.1"
+ "aws-cdk": "^2.65.0"
}
}
diff --git a/poetry.lock b/poetry.lock
index 49a3b94e304..bdae6b00c94 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -43,14 +43,14 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy
[[package]]
name = "aws-cdk-asset-awscli-v1"
-version = "2.2.63"
+version = "2.2.67"
description = "A library that contains the AWS CLI for use in Lambda Layers"
category = "dev"
optional = false
python-versions = "~=3.7"
files = [
- {file = "aws-cdk.asset-awscli-v1-2.2.63.tar.gz", hash = "sha256:76154ade5391f8927c932b609028b28426af34215f144d07576ba35e4eca9442"},
- {file = "aws_cdk.asset_awscli_v1-2.2.63-py3-none-any.whl", hash = "sha256:1ad1d5b7287097f6546902801a40f39b6580c99e5d0eb07dfc5e8ddf428167b0"},
+ {file = "aws-cdk.asset-awscli-v1-2.2.67.tar.gz", hash = "sha256:92d3c003e692a4feafbcf215fd4b8c6321a0dc86f82008d906892ca6eac08851"},
+ {file = "aws_cdk.asset_awscli_v1-2.2.67-py3-none-any.whl", hash = "sha256:250c66fe8611c4cdef115ce32bb02d19d374597c53b299c68bb2a1070dd66727"},
]
[package.dependencies]
@@ -77,14 +77,14 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-asset-node-proxy-agent-v5"
-version = "2.0.52"
+version = "2.0.56"
description = "@aws-cdk/asset-node-proxy-agent-v5"
category = "dev"
optional = false
python-versions = "~=3.7"
files = [
- {file = "aws-cdk.asset-node-proxy-agent-v5-2.0.52.tar.gz", hash = "sha256:1346ce52303e8b8c7c88ce16599a36d947e9546fc6cae0965182594d7b0e600d"},
- {file = "aws_cdk.asset_node_proxy_agent_v5-2.0.52-py3-none-any.whl", hash = "sha256:1a08b261ea2bf10f07fe89a7502686e6be2adea636e6bb1ee1f56b678231fe02"},
+ {file = "aws-cdk.asset-node-proxy-agent-v5-2.0.56.tar.gz", hash = "sha256:4014d618fe22e8cfe026d5307728af434c695c039716ad31544a2b5301a98de1"},
+ {file = "aws_cdk.asset_node_proxy_agent_v5-2.0.56-py3-none-any.whl", hash = "sha256:47bd82e63988a4d1982aa2d04367df10b1e564971847adf3e9d42c0fdce0adeb"},
]
[package.dependencies]
@@ -153,20 +153,20 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-lib"
-version = "2.64.0"
+version = "2.65.0"
description = "Version 2 of the AWS Cloud Development Kit library"
category = "dev"
optional = false
python-versions = "~=3.7"
files = [
- {file = "aws-cdk-lib-2.64.0.tar.gz", hash = "sha256:e21210765b362a0b8a7052fce7bd3a574ea5355a7e763e190c5051ee33b4868a"},
- {file = "aws_cdk_lib-2.64.0-py3-none-any.whl", hash = "sha256:e2de0a80eff201d2eb5326beffa5fb564231ae44eb08ed2b6d4da0c6a324e0e5"},
+ {file = "aws-cdk-lib-2.65.0.tar.gz", hash = "sha256:7d2ea69f827b7f325567109c482ead017f5d2e0fc071d4b352f5db87c24ba010"},
+ {file = "aws_cdk_lib-2.65.0-py3-none-any.whl", hash = "sha256:ea12088a72b858a9bf1aaaf0f6de5e6dfe5962b382324239eb7c830327b84f7c"},
]
[package.dependencies]
-"aws-cdk.asset-awscli-v1" = ">=2.2.52,<3.0.0"
+"aws-cdk.asset-awscli-v1" = ">=2.2.65,<3.0.0"
"aws-cdk.asset-kubectl-v20" = ">=2.1.1,<3.0.0"
-"aws-cdk.asset-node-proxy-agent-v5" = ">=2.0.42,<3.0.0"
+"aws-cdk.asset-node-proxy-agent-v5" = ">=2.0.54,<3.0.0"
constructs = ">=10.0.0,<11.0.0"
jsii = ">=1.74.0,<2.0.0"
publication = ">=0.0.3"
@@ -753,14 +753,14 @@ develop = ["build", "twine"]
[[package]]
name = "flake8-bugbear"
-version = "23.1.20"
+version = "23.2.13"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "flake8-bugbear-23.1.20.tar.gz", hash = "sha256:55902ab5a48c5ea53d8689ecd146eda548e72f2724192b9c1d68f6d975d13c06"},
- {file = "flake8_bugbear-23.1.20-py3-none-any.whl", hash = "sha256:04a115e5f9c8e87c38bdbbcdf9f58223ffe05469c07c9a7bd8633330bc4d078b"},
+ {file = "flake8-bugbear-23.2.13.tar.gz", hash = "sha256:39259814a83f33c8409417ee12dd4050c9c0bb4c8707c12fc18ae62b2f3ddee1"},
+ {file = "flake8_bugbear-23.2.13-py3-none-any.whl", hash = "sha256:f136bd0ca2684f101168bba2310dec541e11aa6b252260c17dcf58d18069a740"},
]
[package.dependencies]
@@ -1315,6 +1315,7 @@ category = "dev"
optional = false
python-versions = "*"
files = [
+ {file = "junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f"},
{file = "junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"},
]
@@ -1544,14 +1545,14 @@ mkdocs = ">=0.17"
[[package]]
name = "mkdocs-material"
-version = "9.0.12"
+version = "9.0.13"
description = "Documentation that simply works"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mkdocs_material-9.0.12-py3-none-any.whl", hash = "sha256:ff4233e4f4da0c879db0dbcb532a690a3f86f5a66a0cfcce99a124e82a462afb"},
- {file = "mkdocs_material-9.0.12.tar.gz", hash = "sha256:4da07b1390c6b78844f1566d723dd045572e645f31b108a1b5062fa7d11aa241"},
+ {file = "mkdocs_material-9.0.13-py3-none-any.whl", hash = "sha256:06e51eba6a090de070a3489890cf1e491d52c04c6ff2b06dd4586c6cdd974a3f"},
+ {file = "mkdocs_material-9.0.13.tar.gz", hash = "sha256:a62696610899d01df091b4d5ad23f9811f878a1f34307d7cea677baf4854c84f"},
]
[package.dependencies]
@@ -1624,14 +1625,14 @@ reports = ["lxml"]
[[package]]
name = "mypy-boto3-appconfig"
-version = "1.26.63"
-description = "Type annotations for boto3.AppConfig 1.26.63 service generated with mypy-boto3-builder 7.12.3"
+version = "1.26.71"
+description = "Type annotations for boto3.AppConfig 1.26.71 service generated with mypy-boto3-builder 7.12.3"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mypy-boto3-appconfig-1.26.63.tar.gz", hash = "sha256:d9dee4b5cdeb9148772fd14f96c480f56bedba688debe8e963fe1274ae130925"},
- {file = "mypy_boto3_appconfig-1.26.63-py3-none-any.whl", hash = "sha256:c329feca6bc8b2647323662161c98816485a223e5a48ace66ab74929bdfce268"},
+ {file = "mypy-boto3-appconfig-1.26.71.tar.gz", hash = "sha256:239c467e2aa6646011fb15c3f632e0988fd9e8af7d46e490f756bd012087dfb7"},
+ {file = "mypy_boto3_appconfig-1.26.71-py3-none-any.whl", hash = "sha256:6e1e722f5cc1e4a16632af279d02c1727c2f36ed4b7df10606a7fc3d0d4c2abe"},
]
[package.dependencies]
@@ -1639,14 +1640,14 @@ typing-extensions = ">=4.1.0"
[[package]]
name = "mypy-boto3-appconfigdata"
-version = "1.26.0.post1"
-description = "Type annotations for boto3.AppConfigData 1.26.0 service generated with mypy-boto3-builder 7.11.10"
+version = "1.26.70"
+description = "Type annotations for boto3.AppConfigData 1.26.70 service generated with mypy-boto3-builder 7.12.3"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mypy-boto3-appconfigdata-1.26.0.post1.tar.gz", hash = "sha256:9f28686ba800c9c5cdbf42b4385164df2de7084758e9ab0850959c7c0a058935"},
- {file = "mypy_boto3_appconfigdata-1.26.0.post1-py3-none-any.whl", hash = "sha256:bacf16229bfdf0c001107cde3c1a2c8219cf708b8cc69b6cb34ad52a29518917"},
+ {file = "mypy-boto3-appconfigdata-1.26.70.tar.gz", hash = "sha256:fafa2cb8500d8291c0a51369f323ce01e15bdf71ef3d07a4a16d5e12b7b545ab"},
+ {file = "mypy_boto3_appconfigdata-1.26.70-py3-none-any.whl", hash = "sha256:c04e24f56bf1cf3127939571875617529b290a7adba7a49cb04a68476f4dfe9e"},
]
[package.dependencies]
@@ -1982,48 +1983,48 @@ files = [
[[package]]
name = "pydantic"
-version = "1.10.4"
+version = "1.10.5"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pydantic-1.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854"},
- {file = "pydantic-1.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817"},
- {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c"},
- {file = "pydantic-1.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e"},
- {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85"},
- {file = "pydantic-1.10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6"},
- {file = "pydantic-1.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d"},
- {file = "pydantic-1.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53"},
- {file = "pydantic-1.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3"},
- {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a"},
- {file = "pydantic-1.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d"},
- {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72"},
- {file = "pydantic-1.10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857"},
- {file = "pydantic-1.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3"},
- {file = "pydantic-1.10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00"},
- {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978"},
- {file = "pydantic-1.10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e"},
- {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa"},
- {file = "pydantic-1.10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8"},
- {file = "pydantic-1.10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903"},
- {file = "pydantic-1.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423"},
- {file = "pydantic-1.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f"},
- {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06"},
- {file = "pydantic-1.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15"},
- {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c"},
- {file = "pydantic-1.10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416"},
- {file = "pydantic-1.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6"},
- {file = "pydantic-1.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d"},
- {file = "pydantic-1.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f"},
- {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024"},
- {file = "pydantic-1.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c"},
- {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28"},
- {file = "pydantic-1.10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f"},
- {file = "pydantic-1.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4"},
- {file = "pydantic-1.10.4-py3-none-any.whl", hash = "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774"},
- {file = "pydantic-1.10.4.tar.gz", hash = "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648"},
+ {file = "pydantic-1.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5920824fe1e21cbb3e38cf0f3dd24857c8959801d1031ce1fac1d50857a03bfb"},
+ {file = "pydantic-1.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3bb99cf9655b377db1a9e47fa4479e3330ea96f4123c6c8200e482704bf1eda2"},
+ {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2185a3b3d98ab4506a3f6707569802d2d92c3a7ba3a9a35683a7709ea6c2aaa2"},
+ {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f582cac9d11c227c652d3ce8ee223d94eb06f4228b52a8adaafa9fa62e73d5c9"},
+ {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c9e5b778b6842f135902e2d82624008c6a79710207e28e86966cd136c621bfee"},
+ {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72ef3783be8cbdef6bca034606a5de3862be6b72415dc5cb1fb8ddbac110049a"},
+ {file = "pydantic-1.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:45edea10b75d3da43cfda12f3792833a3fa70b6eee4db1ed6aed528cef17c74e"},
+ {file = "pydantic-1.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63200cd8af1af2c07964546b7bc8f217e8bda9d0a2ef0ee0c797b36353914984"},
+ {file = "pydantic-1.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:305d0376c516b0dfa1dbefeae8c21042b57b496892d721905a6ec6b79494a66d"},
+ {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd326aff5d6c36f05735c7c9b3d5b0e933b4ca52ad0b6e4b38038d82703d35b"},
+ {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bb0452d7b8516178c969d305d9630a3c9b8cf16fcf4713261c9ebd465af0d73"},
+ {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a9d9155e2a9f38b2eb9374c88f02fd4d6851ae17b65ee786a87d032f87008f8"},
+ {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f836444b4c5ece128b23ec36a446c9ab7f9b0f7981d0d27e13a7c366ee163f8a"},
+ {file = "pydantic-1.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:8481dca324e1c7b715ce091a698b181054d22072e848b6fc7895cd86f79b4449"},
+ {file = "pydantic-1.10.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87f831e81ea0589cd18257f84386bf30154c5f4bed373b7b75e5cb0b5d53ea87"},
+ {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1612e98c6326f10888df951a26ec1a577d8df49ddcaea87773bfbe23ba5cc"},
+ {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58e41dd1e977531ac6073b11baac8c013f3cd8706a01d3dc74e86955be8b2c0c"},
+ {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6a4b0aab29061262065bbdede617ef99cc5914d1bf0ddc8bcd8e3d7928d85bd6"},
+ {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36e44a4de37b8aecffa81c081dbfe42c4d2bf9f6dff34d03dce157ec65eb0f15"},
+ {file = "pydantic-1.10.5-cp37-cp37m-win_amd64.whl", hash = "sha256:261f357f0aecda005934e413dfd7aa4077004a174dafe414a8325e6098a8e419"},
+ {file = "pydantic-1.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b429f7c457aebb7fbe7cd69c418d1cd7c6fdc4d3c8697f45af78b8d5a7955760"},
+ {file = "pydantic-1.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:663d2dd78596c5fa3eb996bc3f34b8c2a592648ad10008f98d1348be7ae212fb"},
+ {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51782fd81f09edcf265823c3bf43ff36d00db246eca39ee765ef58dc8421a642"},
+ {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c428c0f64a86661fb4873495c4fac430ec7a7cef2b8c1c28f3d1a7277f9ea5ab"},
+ {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:76c930ad0746c70f0368c4596020b736ab65b473c1f9b3872310a835d852eb19"},
+ {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3257bd714de9db2102b742570a56bf7978e90441193acac109b1f500290f5718"},
+ {file = "pydantic-1.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:f5bee6c523d13944a1fdc6f0525bc86dbbd94372f17b83fa6331aabacc8fd08e"},
+ {file = "pydantic-1.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:532e97c35719f137ee5405bd3eeddc5c06eb91a032bc755a44e34a712420daf3"},
+ {file = "pydantic-1.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca9075ab3de9e48b75fa8ccb897c34ccc1519177ad8841d99f7fd74cf43be5bf"},
+ {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd46a0e6296346c477e59a954da57beaf9c538da37b9df482e50f836e4a7d4bb"},
+ {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3353072625ea2a9a6c81ad01b91e5c07fa70deb06368c71307529abf70d23325"},
+ {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f9d9b2be177c3cb6027cd67fbf323586417868c06c3c85d0d101703136e6b31"},
+ {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b473d00ccd5c2061fd896ac127b7755baad233f8d996ea288af14ae09f8e0d1e"},
+ {file = "pydantic-1.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:5f3bc8f103b56a8c88021d481410874b1f13edf6e838da607dcb57ecff9b4594"},
+ {file = "pydantic-1.10.5-py3-none-any.whl", hash = "sha256:7c5b94d598c90f2f46b3a983ffb46ab806a67099d118ae0da7ef21a2a4033b28"},
+ {file = "pydantic-1.10.5.tar.gz", hash = "sha256:9e337ac83686645a46db0e825acceea8e02fca4062483f40e9ae178e8bd1103a"},
]
[package.dependencies]
@@ -2718,26 +2719,26 @@ test = ["mypy", "pytest", "typing-extensions"]
[[package]]
name = "types-python-dateutil"
-version = "2.8.19.6"
+version = "2.8.19.7"
description = "Typing stubs for python-dateutil"
category = "dev"
optional = false
python-versions = "*"
files = [
- {file = "types-python-dateutil-2.8.19.6.tar.gz", hash = "sha256:4a6f4cc19ce4ba1a08670871e297bf3802f55d4f129e6aa2443f540b6cf803d2"},
- {file = "types_python_dateutil-2.8.19.6-py3-none-any.whl", hash = "sha256:cfb7d31021c6bce6f3362c69af6e3abb48fe3e08854f02487e844ff910deec2a"},
+ {file = "types-python-dateutil-2.8.19.7.tar.gz", hash = "sha256:7af5a5d1b80ab1dfa0ba4d879facb382e836a62c2d408c2a509be4680fd8b1c8"},
+ {file = "types_python_dateutil-2.8.19.7-py3-none-any.whl", hash = "sha256:669751e1e6d4f3dbbff471231740e7ecdae2135b604383e477fe31fd56223967"},
]
[[package]]
name = "types-requests"
-version = "2.28.11.12"
+version = "2.28.11.13"
description = "Typing stubs for requests"
category = "dev"
optional = false
python-versions = "*"
files = [
- {file = "types-requests-2.28.11.12.tar.gz", hash = "sha256:fd530aab3fc4f05ee36406af168f0836e6f00f1ee51a0b96b7311f82cb675230"},
- {file = "types_requests-2.28.11.12-py3-none-any.whl", hash = "sha256:dbc2933635860e553ffc59f5e264264981358baffe6342b925e3eb8261f866ee"},
+ {file = "types-requests-2.28.11.13.tar.gz", hash = "sha256:3fd332842e8759ea5f7eb7789df8aa772ba155216ccf10ef4aa3b0e5b42e1b46"},
+ {file = "types_requests-2.28.11.13-py3-none-any.whl", hash = "sha256:94896f6f8e9f3db11e422c6e3e4abbc5d7ccace853eac74b23bdd65eeee3cdee"},
]
[package.dependencies]
@@ -2957,4 +2958,4 @@ validation = ["fastjsonschema"]
[metadata]
lock-version = "2.0"
python-versions = "^3.7.4"
-content-hash = "62a6b0896bad16de0b814e025384cc7c078c72cead1e5c4926700c118d8b7dda"
+content-hash = "9a325439a5db67bb46298936cae243c40613cf8db568e36a42dbeeda1d2bbc4e"
diff --git a/pyproject.toml b/pyproject.toml
index dbef79c46ef..00db1a5b685 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aws_lambda_powertools"
-version = "2.8.0"
+version = "2.9.0"
description = "A suite of utilities for AWS Lambda functions to ease adopting best practices such as tracing, structured logging, custom metrics, batching, idempotency, feature flags, and more."
authors = ["Amazon Web Services"]
include = ["aws_lambda_powertools/py.typed", "THIRD-PARTY-LICENSES"]
@@ -58,18 +58,18 @@ bandit = "^1.7.1"
radon = "^5.1.0"
xenon = "^0.9.0"
flake8-eradicate = "^1.2.1"
-flake8-bugbear = "^23.1.20"
+flake8-bugbear = "^23.2.13"
mkdocs-git-revision-date-plugin = "^0.3.2"
mike = "^1.1.2"
retry = "^0.9.2"
pytest-xdist = "^3.2.0"
-aws-cdk-lib = "^2.64.0"
+aws-cdk-lib = "^2.65.0"
"aws-cdk.aws-apigatewayv2-alpha" = "^2.38.1-alpha.0"
"aws-cdk.aws-apigatewayv2-integrations-alpha" = "^2.38.1-alpha.0"
"aws-cdk.aws-apigatewayv2-authorizers-alpha" = "^2.38.1-alpha.0"
pytest-benchmark = "^4.0.0"
python-snappy = "^0.6.1"
-mypy-boto3-appconfig = "^1.26.63"
+mypy-boto3-appconfig = "^1.26.71"
mypy-boto3-cloudformation = "^1.26.57"
mypy-boto3-cloudwatch = "^1.26.52"
mypy-boto3-dynamodb = "^1.26.24"
@@ -81,10 +81,10 @@ mypy-boto3-s3 = "^1.26.62"
mypy-boto3-xray = "^1.26.11"
types-requests = "^2.28.11"
typing-extensions = "^4.4.0"
-mkdocs-material = "^9.0.12"
+mkdocs-material = "^9.0.13"
filelock = "^3.9.0"
checksumdir = "^1.2.0"
-mypy-boto3-appconfigdata = "^1.26.0"
+mypy-boto3-appconfigdata = "^1.26.70"
importlib-metadata = "^6.0"
ijson = "^3.2.0"
typed-ast = { version = "^1.5.4", python = "< 3.8"}
diff --git a/tests/functional/test_logger.py b/tests/functional/test_logger.py
index 0e576752508..412a9358553 100644
--- a/tests/functional/test_logger.py
+++ b/tests/functional/test_logger.py
@@ -251,6 +251,19 @@ def test_logger_append_duplicated(stdout, service_name):
assert "new_value" == log["request_id"]
+def test_logger_honors_given_exception_keys(stdout, service_name):
+ # GIVEN Logger is initialized with exception and exception_name fields
+ logger = Logger(service=service_name, stream=stdout)
+
+ # WHEN log level info
+ logger.info("log", exception="exception_value", exception_name="exception_name_value")
+
+ # THEN log statements should have these keys
+ log = capture_logging_output(stdout)
+ assert "exception_value" == log["exception"]
+ assert "exception_name_value" == log["exception_name"]
+
+
def test_logger_invalid_sampling_rate(service_name):
# GIVEN Logger is initialized
# WHEN sampling_rate non-numeric value
diff --git a/tests/functional/test_metrics.py b/tests/functional/test_metrics.py
index 2a53b42cd16..c0c41f3bf88 100644
--- a/tests/functional/test_metrics.py
+++ b/tests/functional/test_metrics.py
@@ -728,7 +728,10 @@ def lambda_handler(evt, context):
warnings.simplefilter("default")
lambda_handler({}, {})
assert len(w) == 1
- assert str(w[-1].message) == "No metrics to publish, skipping"
+ assert str(w[-1].message) == (
+ "No application metrics to publish. The cold-start metric may be published if enabled. "
+ "If application metrics should never be empty, consider using 'raise_on_empty_metrics'"
+ )
def test_log_metrics_with_implicit_dimensions_called_twice(capsys, metric, namespace, service):
diff --git a/tests/functional/test_utilities_batch.py b/tests/functional/test_utilities_batch.py
index 6dcfc3d179d..c98d59a7042 100644
--- a/tests/functional/test_utilities_batch.py
+++ b/tests/functional/test_utilities_batch.py
@@ -1,4 +1,5 @@
import json
+import uuid
from random import randint
from typing import Any, Awaitable, Callable, Dict, Optional
@@ -9,6 +10,7 @@
AsyncBatchProcessor,
BatchProcessor,
EventType,
+ SqsFifoPartialProcessor,
async_batch_processor,
batch_processor,
)
@@ -40,7 +42,7 @@
def sqs_event_factory() -> Callable:
def factory(body: str):
return {
- "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
+ "messageId": f"{uuid.uuid4()}",
"receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a",
"body": body,
"attributes": {
@@ -654,6 +656,48 @@ def lambda_handler(event, context):
assert "All records failed processing. " in str(e.value)
+def test_sqs_fifo_batch_processor_middleware_success_only(sqs_event_factory, record_handler):
+ # GIVEN
+ first_record = SQSRecord(sqs_event_factory("success"))
+ second_record = SQSRecord(sqs_event_factory("success"))
+ event = {"Records": [first_record.raw_event, second_record.raw_event]}
+
+ processor = SqsFifoPartialProcessor()
+
+ @batch_processor(record_handler=record_handler, processor=processor)
+ def lambda_handler(event, context):
+ return processor.response()
+
+ # WHEN
+ result = lambda_handler(event, {})
+
+ # THEN
+ assert result["batchItemFailures"] == []
+
+
+def test_sqs_fifo_batch_processor_middleware_with_failure(sqs_event_factory, record_handler):
+ # GIVEN
+ first_record = SQSRecord(sqs_event_factory("success"))
+ second_record = SQSRecord(sqs_event_factory("fail"))
+ # this would normally succeed, but since it's a FIFO queue, it will be marked as failure
+ third_record = SQSRecord(sqs_event_factory("success"))
+ event = {"Records": [first_record.raw_event, second_record.raw_event, third_record.raw_event]}
+
+ processor = SqsFifoPartialProcessor()
+
+ @batch_processor(record_handler=record_handler, processor=processor)
+ def lambda_handler(event, context):
+ return processor.response()
+
+ # WHEN
+ result = lambda_handler(event, {})
+
+ # THEN
+ assert len(result["batchItemFailures"]) == 2
+ assert result["batchItemFailures"][0]["itemIdentifier"] == second_record.message_id
+ assert result["batchItemFailures"][1]["itemIdentifier"] == third_record.message_id
+
+
def test_async_batch_processor_middleware_success_only(sqs_event_factory, async_record_handler):
# GIVEN
first_record = SQSRecord(sqs_event_factory("success"))