diff --git a/.github/workflows/secure_workflows.yml b/.github/workflows/secure_workflows.yml
index bbe274806ea..acb717a2182 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@6ca5574367befbc9efdb2fa25978084159c5902d # v1.3.0
+ uses: zgosalvez/github-actions-ensure-sha-pinned-actions@af2eb3226618e2494e3d9084f515ad6dcf16e229 # v2.0.1
with:
# Trusted GitHub Actions and/or organizations
allowlist: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 451d2eaef4f..e9252bcb325 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,45 @@
## Bug Fixes
+* **event_handlers:** omit explicit None HTTP header values ([#1793](https://github.com/awslabs/aws-lambda-powertools-python/issues/1793))
+
+## Documentation
+
+* **idempotency:** fix, improve, and increase visibility for batch integration ([#1776](https://github.com/awslabs/aws-lambda-powertools-python/issues/1776))
+* **validation:** fix broken link; enrich built-in jmespath links ([#1777](https://github.com/awslabs/aws-lambda-powertools-python/issues/1777))
+
+## Features
+
+* **logger:** unwrap event from common models if asked to log ([#1778](https://github.com/awslabs/aws-lambda-powertools-python/issues/1778))
+
+## Maintenance
+
+* **common:** reusable function to extract event from models
+* **deps:** bump certifi from 2022.9.24 to 2022.12.7 ([#1768](https://github.com/awslabs/aws-lambda-powertools-python/issues/1768))
+* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 1.4.0 to 2.0.1 ([#1752](https://github.com/awslabs/aws-lambda-powertools-python/issues/1752))
+* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 1.3.0 to 1.4.0 ([#1749](https://github.com/awslabs/aws-lambda-powertools-python/issues/1749))
+* **deps-dev:** bump aws-cdk-lib from 2.53.0 to 2.54.0 ([#1764](https://github.com/awslabs/aws-lambda-powertools-python/issues/1764))
+* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.0.post1 to 1.26.17 ([#1753](https://github.com/awslabs/aws-lambda-powertools-python/issues/1753))
+* **deps-dev:** bump pytest-asyncio from 0.20.2 to 0.20.3 ([#1767](https://github.com/awslabs/aws-lambda-powertools-python/issues/1767))
+* **deps-dev:** bump isort from 5.10.1 to 5.11.2 ([#1782](https://github.com/awslabs/aws-lambda-powertools-python/issues/1782))
+* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.13.post16 to 1.26.24 ([#1765](https://github.com/awslabs/aws-lambda-powertools-python/issues/1765))
+* **deps-dev:** bump mypy-boto3-logs from 1.26.17 to 1.26.27 ([#1775](https://github.com/awslabs/aws-lambda-powertools-python/issues/1775))
+* **deps-dev:** bump mypy-boto3-cloudwatch from 1.26.17 to 1.26.30 ([#1785](https://github.com/awslabs/aws-lambda-powertools-python/issues/1785))
+* **deps-dev:** bump flake8-bugbear from 22.10.27 to 22.12.6 ([#1760](https://github.com/awslabs/aws-lambda-powertools-python/issues/1760))
+* **deps-dev:** bump filelock from 3.8.0 to 3.8.2 ([#1759](https://github.com/awslabs/aws-lambda-powertools-python/issues/1759))
+* **deps-dev:** bump pytest-xdist from 3.0.2 to 3.1.0 ([#1758](https://github.com/awslabs/aws-lambda-powertools-python/issues/1758))
+* **deps-dev:** bump mkdocs-material from 8.5.10 to 8.5.11 ([#1756](https://github.com/awslabs/aws-lambda-powertools-python/issues/1756))
+* **deps-dev:** bump importlib-metadata from 4.13.0 to 5.1.0 ([#1750](https://github.com/awslabs/aws-lambda-powertools-python/issues/1750))
+* **deps-dev:** bump aws-cdk-lib from 2.54.0 to 2.55.1 ([#1787](https://github.com/awslabs/aws-lambda-powertools-python/issues/1787))
+* **deps-dev:** bump flake8-black from 0.3.3 to 0.3.5 ([#1738](https://github.com/awslabs/aws-lambda-powertools-python/issues/1738))
+* **deps-dev:** bump isort from 5.11.2 to 5.11.3 ([#1788](https://github.com/awslabs/aws-lambda-powertools-python/issues/1788))
+* **tests:** move shared_functions to unit tests
+
+
+
+## [v2.4.0] - 2022-11-24
+## Bug Fixes
+
* **ci:** use gh-pages env as official docs are wrong
* **ci:** api docs path
@@ -36,6 +75,10 @@
* **deps-dev:** bump mkdocs-material from 8.5.9 to 8.5.10 ([#1731](https://github.com/awslabs/aws-lambda-powertools-python/issues/1731))
* **governance:** remove markdown rendering from docs issue template
+## Regression
+
+* **ci:** new gh-pages beta doesn't work either; reverting as gh-pages is disrupted
+
## [v2.3.1] - 2022-11-21
@@ -2665,7 +2708,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.3.1...HEAD
+[Unreleased]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.4.0...HEAD
+[v2.4.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.3.1...v2.4.0
[v2.3.1]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.3.0...v2.3.1
[v2.3.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.2.0...v2.3.0
[v2.2.0]: https://github.com/awslabs/aws-lambda-powertools-python/compare/v2.1.0...v2.2.0
diff --git a/aws_lambda_powertools/logging/logger.py b/aws_lambda_powertools/logging/logger.py
index bafde28e65c..f9c33c70ba6 100644
--- a/aws_lambda_powertools/logging/logger.py
+++ b/aws_lambda_powertools/logging/logger.py
@@ -26,7 +26,11 @@
import jmespath
from ..shared import constants
-from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
+from ..shared.functions import (
+ extract_event_from_common_models,
+ resolve_env_var_choice,
+ resolve_truthy_env_var_choice,
+)
from ..shared.types import AnyCallableT
from .exceptions import InvalidLoggerSamplingRateError
from .filters import SuppressFilter
@@ -433,7 +437,7 @@ def decorate(event, context, *args, **kwargs):
if log_event:
logger.debug("Event received")
- self.info(getattr(event, "raw_event", event))
+ self.info(extract_event_from_common_models(event))
return lambda_handler(event, context, *args, **kwargs)
diff --git a/aws_lambda_powertools/shared/functions.py b/aws_lambda_powertools/shared/functions.py
index 884edb37e35..b1c53d989bb 100644
--- a/aws_lambda_powertools/shared/functions.py
+++ b/aws_lambda_powertools/shared/functions.py
@@ -1,10 +1,13 @@
+from __future__ import annotations
+
import base64
+import dataclasses
import itertools
import logging
import os
import warnings
from binascii import Error as BinAsciiError
-from typing import Dict, Generator, Optional, Union, overload
+from typing import Any, Dict, Generator, Optional, Union, overload
from aws_lambda_powertools.shared import constants
@@ -121,3 +124,43 @@ def powertools_debug_is_set() -> bool:
def slice_dictionary(data: Dict, chunk_size: int) -> Generator[Dict, None, None]:
for _ in range(0, len(data), chunk_size):
yield {dict_key: data[dict_key] for dict_key in itertools.islice(data, chunk_size)}
+
+
+def extract_event_from_common_models(data: Any) -> Dict | Any:
+ """Extract raw event from common types used in Powertools
+
+ If event cannot be extracted, return received data as is.
+
+ Common models:
+
+ - Event Source Data Classes (DictWrapper)
+ - Python Dataclasses
+ - Pydantic Models (BaseModel)
+
+ Parameters
+ ----------
+ data : Any
+ Original event, a potential instance of DictWrapper/BaseModel/Dataclass
+
+ Notes
+ -----
+
+ Why not using static type for function argument?
+
+ DictWrapper would cause a circular import. Pydantic BaseModel could
+ cause a ModuleNotFound or trigger init reflection worsening cold start.
+ """
+ # Short-circuit most common type first for perf
+ if isinstance(data, dict):
+ return data
+
+ # Is it an Event Source Data Class?
+ if getattr(data, "raw_event", None):
+ return data.raw_event
+
+ # Is it a Pydantic Model?
+ if callable(getattr(data, "dict", None)):
+ return data.dict()
+
+ # Is it a Dataclass? If not return as is
+ return dataclasses.asdict(data) if dataclasses.is_dataclass(data) else data
diff --git a/aws_lambda_powertools/shared/headers_serializer.py b/aws_lambda_powertools/shared/headers_serializer.py
index 796fd9aeae3..b4e9f7c7c5f 100644
--- a/aws_lambda_powertools/shared/headers_serializer.py
+++ b/aws_lambda_powertools/shared/headers_serializer.py
@@ -41,6 +41,10 @@ def serialize(self, headers: Dict[str, Union[str, List[str]]], cookies: List[Coo
# Duplicate headers are combined with commas and included in the headers field.
combined_headers: Dict[str, str] = {}
for key, values in headers.items():
+ # omit headers with explicit null values
+ if values is None:
+ continue
+
if isinstance(values, str):
combined_headers[key] = values
else:
@@ -60,13 +64,15 @@ def serialize(self, headers: Dict[str, Union[str, List[str]]], cookies: List[Coo
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers-response
"""
payload: Dict[str, List[str]] = defaultdict(list)
-
for key, values in headers.items():
+ # omit headers with explicit null values
+ if values is None:
+ continue
+
if isinstance(values, str):
payload[key].append(values)
else:
- for value in values:
- payload[key].append(value)
+ payload[key].extend(values)
if cookies:
payload.setdefault("Set-Cookie", [])
@@ -98,6 +104,10 @@ def serialize(self, headers: Dict[str, Union[str, List[str]]], cookies: List[Coo
payload["headers"]["Set-Cookie"] = str(cookies[-1])
for key, values in headers.items():
+ # omit headers with explicit null values
+ if values is None:
+ continue
+
if isinstance(values, str):
payload["headers"][key] = values
else:
diff --git a/docs/index.md b/docs/index.md
index 11d5a5dafa2..71cfe00a7f6 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:16**](#){: .copyMe}:clipboard:
-* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16**](#){: .copyMe}:clipboard:
+* **Lambda Layer (x86_64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17**](#){: .copyMe}:clipboard:
+* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17**](#){: .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:16](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:16](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:17](#){: .copyMe}:clipboard: |
=== "arm64"
| Region | Layer ARN |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
- | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:16](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17](#){: .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:16
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17
```
=== "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:16
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17
```
=== "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:16"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17"
)
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:16"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17"]
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:16
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17
❯ 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:16
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:17
? 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:16 --region {region}
+ aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:17 --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:16
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17
```
=== "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:16
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17
```
=== "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:16"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17"
)
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:16"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17"]
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:16
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17
❯ 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:16
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17
? 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:16 --region {region}
+ aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:17 --region {region}
```
The pre-signed URL to download this Lambda Layer will be within `Location` key.
diff --git a/docs/utilities/idempotency.md b/docs/utilities/idempotency.md
index 2e3f5f1e88d..89cd3003b77 100644
--- a/docs/utilities/idempotency.md
+++ b/docs/utilities/idempotency.md
@@ -132,75 +132,6 @@ When using `idempotent_function`, you must tell us which keyword parameter in yo
DynamoDB Persistency layer uses a Resource client [which is not thread-safe](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html?highlight=multithreading#multithreading-or-multiprocessing-with-resources){target="_blank"}.
-=== "batch_sample.py"
-
- This example also demonstrates how you can integrate with [Batch utility](batch.md), so you can process each record in an idempotent manner.
-
- ```python hl_lines="4-5 16 21 30"
- from aws_lambda_powertools.utilities.batch import (BatchProcessor, EventType,
- batch_processor)
- from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
- from aws_lambda_powertools.utilities.idempotency import (
- DynamoDBPersistenceLayer, IdempotencyConfig, idempotent_function)
-
-
- processor = BatchProcessor(event_type=EventType.SQS)
- dynamodb = DynamoDBPersistenceLayer(table_name="idem")
- config = IdempotencyConfig(
- event_key_jmespath="messageId", # see Choosing a payload subset section
- use_local_cache=True,
- )
-
-
- @idempotent_function(data_keyword_argument="record", config=config, persistence_store=dynamodb)
- def record_handler(record: SQSRecord):
- return {"message": record["body"]}
-
-
- @idempotent_function(data_keyword_argument="data", config=config, persistence_store=dynamodb)
- def dummy(arg_one, arg_two, data: dict, **kwargs):
- return {"data": data}
-
-
- @batch_processor(record_handler=record_handler, processor=processor)
- def lambda_handler(event, context):
- config.register_lambda_context(context) # see Lambda timeouts section
- # `data` parameter must be called as a keyword argument to work
- dummy("hello", "universe", data="test")
- return processor.response()
- ```
-
-=== "Batch event"
-
- ```json hl_lines="4"
- {
- "Records": [
- {
- "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
- "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
- "body": "Test message.",
- "attributes": {
- "ApproximateReceiveCount": "1",
- "SentTimestamp": "1545082649183",
- "SenderId": "AIDAIENQZJOLO23YVJ4VO",
- "ApproximateFirstReceiveTimestamp": "1545082649185"
- },
- "messageAttributes": {
- "testAttr": {
- "stringValue": "100",
- "binaryValue": "base64Str",
- "dataType": "Number"
- }
- },
- "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
- "eventSource": "aws:sqs",
- "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
- "awsRegion": "us-east-2"
- }
- ]
- }
- ```
-
=== "dataclass_sample.py"
```python hl_lines="3-4 23 33"
@@ -276,6 +207,82 @@ When using `idempotent_function`, you must tell us which keyword parameter in yo
process_order(order=order)
```
+#### Batch integration
+
+You can can easily integrate with [Batch utility](batch.md) via context manager. This ensures that you process each record in an idempotent manner, and guard against a [Lambda timeout](#lambda-timeouts) idempotent situation.
+
+???+ "Choosing an unique batch record attribute"
+ In this example, we choose `messageId` as our idempotency token since we know it'll be unique.
+
+ Depending on your use case, it might be more accurate [to choose another field](#choosing-a-payload-subset-for-idempotency) your producer intentionally set to define uniqueness.
+
+=== "batch_sample.py"
+
+ ```python hl_lines="3-4 10 15 21 25-26 29 31"
+ from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType
+ from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord
+ from aws_lambda_powertools.utilities.idempotency import (
+ DynamoDBPersistenceLayer, IdempotencyConfig, idempotent_function)
+
+
+ processor = BatchProcessor(event_type=EventType.SQS)
+ dynamodb = DynamoDBPersistenceLayer(table_name="idem")
+ config = IdempotencyConfig(
+ event_key_jmespath="messageId", # see Choosing a payload subset section
+ use_local_cache=True,
+ )
+
+
+ @idempotent_function(data_keyword_argument="record", config=config, persistence_store=dynamodb)
+ def record_handler(record: SQSRecord):
+ return {"message": record.body}
+
+
+ def lambda_handler(event, context):
+ config.register_lambda_context(context) # see Lambda timeouts section
+
+ # with Lambda context registered for Idempotency
+ # we can now kick in the Bach processing logic
+ batch = event["Records"]
+ with processor(records=batch, handler=record_handler):
+ # in case you want to access each record processed by your record_handler
+ # otherwise ignore the result variable assignment
+ processed_messages = processor.process()
+
+ return processor.response()
+ ```
+
+=== "batch_event.json"
+
+ ```json hl_lines="4"
+ {
+ "Records": [
+ {
+ "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
+ "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
+ "body": "Test message.",
+ "attributes": {
+ "ApproximateReceiveCount": "1",
+ "SentTimestamp": "1545082649183",
+ "SenderId": "AIDAIENQZJOLO23YVJ4VO",
+ "ApproximateFirstReceiveTimestamp": "1545082649185"
+ },
+ "messageAttributes": {
+ "testAttr": {
+ "stringValue": "100",
+ "binaryValue": "base64Str",
+ "dataType": "Number"
+ }
+ },
+ "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
+ "eventSource": "aws:sqs",
+ "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
+ "awsRegion": "us-east-2"
+ }
+ ]
+ }
+ ```
+
### Choosing a payload subset for idempotency
???+ tip "Tip: Dealing with always changing payloads"
@@ -982,6 +989,10 @@ class DynamoDBPersistenceLayer(BasePersistenceLayer):
## Compatibility with other utilities
+### Batch
+
+See [Batch integration](#batch-integration) above.
+
### Validation utility
The idempotency utility can be used with the `validator` decorator. Ensure that idempotency is the innermost decorator.
diff --git a/docs/utilities/validation.md b/docs/utilities/validation.md
index dcd35b9ab7a..ca5907a5fe1 100644
--- a/docs/utilities/validation.md
+++ b/docs/utilities/validation.md
@@ -194,7 +194,7 @@ For each format defined in a dictionary key, you must use a regex, or a function
You might have events or responses that contain non-encoded JSON, where you need to decode before validating them.
-You can use our built-in [JMESPath functions](/utilities/jmespath_functions) within your expressions to do exactly that to decode JSON Strings, base64, and uncompress gzip data.
+You can use our built-in [JMESPath functions](./jmespath_functions.md) within your expressions to do exactly that to [deserialize JSON Strings](./jmespath_functions.md#powertools_json-function), [decode base64](./jmespath_functions.md#powertools_base64-function), and [decompress gzip data](./jmespath_functions.md#powertools_base64_gzip-function).
???+ info
- We use these for built-in envelopes to easily to decode and unwrap events from sources like Kinesis, CloudWatch Logs, etc.
+ We use these for [built-in envelopes](#built-in-envelopes) to easily to decode and unwrap events from sources like Kinesis, CloudWatch Logs, etc.
diff --git a/poetry.lock b/poetry.lock
index 7246cf056d6..03eabb7b81c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -14,14 +14,14 @@ tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy
[[package]]
name = "aws-cdk-asset-awscli-v1"
-version = "2.2.17"
+version = "2.2.31"
description = "A library that contains the AWS CLI for use in Lambda Layers"
category = "dev"
optional = false
python-versions = "~=3.7"
[package.dependencies]
-jsii = ">=1.71.0,<2.0.0"
+jsii = ">=1.72.0,<2.0.0"
publication = ">=0.0.3"
typeguard = ">=2.13.3,<2.14.0"
@@ -40,27 +40,27 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-asset-node-proxy-agent-v5"
-version = "2.0.23"
+version = "2.0.38"
description = "@aws-cdk/asset-node-proxy-agent-v5"
category = "dev"
optional = false
python-versions = "~=3.7"
[package.dependencies]
-jsii = ">=1.71.0,<2.0.0"
+jsii = ">=1.72.0,<2.0.0"
publication = ">=0.0.3"
typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-aws-apigatewayv2-alpha"
-version = "2.51.1a0"
+version = "2.53.0a0"
description = "The CDK Construct Library for AWS::APIGatewayv2"
category = "dev"
optional = false
python-versions = "~=3.7"
[package.dependencies]
-aws-cdk-lib = ">=2.51.1,<3.0.0"
+aws-cdk-lib = ">=2.53.0,<3.0.0"
constructs = ">=10.0.0,<11.0.0"
jsii = ">=1.71.0,<2.0.0"
publication = ">=0.0.3"
@@ -68,15 +68,15 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-aws-apigatewayv2-integrations-alpha"
-version = "2.51.1a0"
+version = "2.53.0a0"
description = "Integrations for AWS APIGateway V2"
category = "dev"
optional = false
python-versions = "~=3.7"
[package.dependencies]
-"aws-cdk.aws-apigatewayv2-alpha" = "2.51.1.a0"
-aws-cdk-lib = ">=2.51.1,<3.0.0"
+"aws-cdk.aws-apigatewayv2-alpha" = "2.53.0.a0"
+aws-cdk-lib = ">=2.53.0,<3.0.0"
constructs = ">=10.0.0,<11.0.0"
jsii = ">=1.71.0,<2.0.0"
publication = ">=0.0.3"
@@ -84,24 +84,24 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-cdk-lib"
-version = "2.51.1"
+version = "2.55.1"
description = "Version 2 of the AWS Cloud Development Kit library"
category = "dev"
optional = false
python-versions = "~=3.7"
[package.dependencies]
-"aws-cdk.asset-awscli-v1" = ">=2.2.9,<3.0.0"
+"aws-cdk.asset-awscli-v1" = ">=2.2.30,<3.0.0"
"aws-cdk.asset-kubectl-v20" = ">=2.1.1,<3.0.0"
-"aws-cdk.asset-node-proxy-agent-v5" = ">=2.0.15,<3.0.0"
+"aws-cdk.asset-node-proxy-agent-v5" = ">=2.0.38,<3.0.0"
constructs = ">=10.0.0,<11.0.0"
-jsii = ">=1.71.0,<2.0.0"
+jsii = ">=1.72.0,<2.0.0"
publication = ">=0.0.3"
typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "aws-sam-translator"
-version = "1.54.0"
+version = "1.55.0"
description = "AWS SAM Translator is a library that transform SAM templates into AWS CloudFormation templates"
category = "dev"
optional = false
@@ -170,14 +170,14 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "boto3"
-version = "1.26.16"
+version = "1.26.18"
description = "The AWS SDK for Python"
category = "main"
optional = false
python-versions = ">= 3.7"
[package.dependencies]
-botocore = ">=1.29.16,<1.30.0"
+botocore = ">=1.29.18,<1.30.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.6.0,<0.7.0"
@@ -186,7 +186,7 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.29.16"
+version = "1.29.18"
description = "Low-level, data-driven core of boto 3."
category = "main"
optional = false
@@ -215,7 +215,7 @@ typing_extensions = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "certifi"
-version = "2022.9.24"
+version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "dev"
optional = false
@@ -280,7 +280,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7
[[package]]
name = "constructs"
-version = "10.1.169"
+version = "10.1.174"
description = "A programming model for software-defined state"
category = "dev"
optional = false
@@ -356,15 +356,15 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc
[[package]]
name = "filelock"
-version = "3.8.0"
+version = "3.8.2"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
-testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]
+docs = ["furo (>=2022.9.29)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
+testing = ["covdefaults (>=2.2.2)", "coverage (>=6.5)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"]
[[package]]
name = "flake8"
@@ -380,6 +380,19 @@ mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.7.0,<2.8.0"
pyflakes = ">=2.3.0,<2.4.0"
+[[package]]
+name = "flake8"
+version = "5.0.4"
+description = "the modular source code checker: pep8 pyflakes and co"
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.9.0,<2.10.0"
+pyflakes = ">=2.5.0,<2.6.0"
+
[[package]]
name = "flake8-black"
version = "0.3.5"
@@ -398,7 +411,7 @@ develop = ["build", "twine"]
[[package]]
name = "flake8-bugbear"
-version = "22.10.27"
+version = "22.12.6"
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
@@ -542,7 +555,7 @@ python-versions = "*"
[[package]]
name = "importlib-metadata"
-version = "4.13.0"
+version = "5.1.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
@@ -567,11 +580,11 @@ python-versions = "*"
[[package]]
name = "isort"
-version = "5.10.1"
+version = "5.11.3"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
-python-versions = ">=3.6.1,<4.0"
+python-versions = ">=3.7.0"
[package.extras]
colors = ["colorama (>=0.4.3,<0.5.0)"]
@@ -616,7 +629,7 @@ pbr = "*"
[[package]]
name = "jsii"
-version = "1.71.0"
+version = "1.72.0"
description = "Python client for jsii runtime"
category = "dev"
optional = false
@@ -756,6 +769,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
[[package]]
name = "mergedeep"
version = "1.3.4"
@@ -823,7 +844,7 @@ mkdocs = ">=0.17"
[[package]]
name = "mkdocs-material"
-version = "8.5.10"
+version = "8.5.11"
description = "Documentation that simply works"
category = "dev"
optional = false
@@ -900,8 +921,8 @@ typing-extensions = ">=4.1.0"
[[package]]
name = "mypy-boto3-cloudwatch"
-version = "1.26.0.post1"
-description = "Type annotations for boto3.CloudWatch 1.26.0 service generated with mypy-boto3-builder 7.11.10"
+version = "1.26.30"
+description = "Type annotations for boto3.CloudWatch 1.26.30 service generated with mypy-boto3-builder 7.12.0"
category = "dev"
optional = false
python-versions = ">=3.7"
@@ -911,8 +932,8 @@ typing-extensions = ">=4.1.0"
[[package]]
name = "mypy-boto3-dynamodb"
-version = "1.26.13.post16"
-description = "Type annotations for boto3.DynamoDB 1.26.13 service generated with mypy-boto3-builder 7.11.11"
+version = "1.26.24"
+description = "Type annotations for boto3.DynamoDB 1.26.24 service generated with mypy-boto3-builder 7.11.11"
category = "dev"
optional = false
python-versions = ">=3.7"
@@ -922,8 +943,8 @@ typing-extensions = ">=4.1.0"
[[package]]
name = "mypy-boto3-lambda"
-version = "1.26.12"
-description = "Type annotations for boto3.Lambda 1.26.12 service generated with mypy-boto3-builder 7.11.10"
+version = "1.26.18"
+description = "Type annotations for boto3.Lambda 1.26.18 service generated with mypy-boto3-builder 7.11.11"
category = "dev"
optional = false
python-versions = ">=3.7"
@@ -933,8 +954,8 @@ typing-extensions = ">=4.1.0"
[[package]]
name = "mypy-boto3-logs"
-version = "1.26.3"
-description = "Type annotations for boto3.CloudWatchLogs 1.26.3 service generated with mypy-boto3-builder 7.11.10"
+version = "1.26.27"
+description = "Type annotations for boto3.CloudWatchLogs 1.26.27 service generated with mypy-boto3-builder 7.12.0"
category = "dev"
optional = false
python-versions = ">=3.7"
@@ -1107,6 +1128,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pycodestyle"
+version = "2.9.1"
+description = "Python style guide checker"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
[[package]]
name = "pydantic"
version = "1.10.2"
@@ -1130,6 +1159,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pyflakes"
+version = "2.5.0"
+description = "passive checker of Python programs"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
[[package]]
name = "pygments"
version = "2.13.0"
@@ -1194,7 +1231,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
[[package]]
name = "pytest-asyncio"
-version = "0.20.2"
+version = "0.20.3"
description = "Pytest support for asyncio"
category = "dev"
optional = false
@@ -1205,6 +1242,7 @@ pytest = ">=6.1.0"
typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""}
[package.extras]
+docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
@@ -1255,11 +1293,11 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "pytest-xdist"
-version = "3.0.2"
-description = "pytest xdist plugin for distributed testing and loop-on-failing modes"
+version = "3.1.0"
+description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
execnet = ">=1.1"
@@ -1531,7 +1569,7 @@ requests = ">=2.0,<3.0"
[[package]]
name = "zipp"
-version = "3.10.0"
+version = "3.11.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
@@ -1551,7 +1589,7 @@ validation = ["fastjsonschema"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7.4"
-content-hash = "502dbe48f1ed8a7e4d4c755adf02f0641eef20ab6eecffe19a50cafcca0f237d"
+content-hash = "8ecbd7ba0ea29451498eee0b35c7796b75e4e3fa87e8924c5bd241dc2805ca73"
[metadata.files]
attrs = [
@@ -1559,33 +1597,33 @@ attrs = [
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
aws-cdk-asset-awscli-v1 = [
- {file = "aws-cdk.asset-awscli-v1-2.2.17.tar.gz", hash = "sha256:6d67c37d2989e1dfb3c376fdb773121af3879e363838991b47a5778f4ff1f114"},
- {file = "aws_cdk.asset_awscli_v1-2.2.17-py3-none-any.whl", hash = "sha256:8e71c77817d1c2315af0b17756384a7542737d621b2046a686eb310be5c17cc9"},
+ {file = "aws-cdk.asset-awscli-v1-2.2.31.tar.gz", hash = "sha256:c7e01034ae5f128f6853332a61282e51d8112bd975df6897436678bf358eceed"},
+ {file = "aws_cdk.asset_awscli_v1-2.2.31-py3-none-any.whl", hash = "sha256:3194fbf956578d85d16cd9bb0541f9011bc5564baa9c4495b47dbe3889165d44"},
]
aws-cdk-asset-kubectl-v20 = [
{file = "aws-cdk.asset-kubectl-v20-2.1.1.tar.gz", hash = "sha256:9834cdb150c5590aea4e5eba6de2a89b4c60617451181c524810c5a75154565c"},
{file = "aws_cdk.asset_kubectl_v20-2.1.1-py3-none-any.whl", hash = "sha256:a2fad1a5a35a94a465efe60859f91e45dacc33261fb9bbf1cf9bbc6e2f70e9d6"},
]
aws-cdk-asset-node-proxy-agent-v5 = [
- {file = "aws-cdk.asset-node-proxy-agent-v5-2.0.23.tar.gz", hash = "sha256:98728eba7747af889b803462c26f46a831fd24abba0bb04b267aea5a4a9af738"},
- {file = "aws_cdk.asset_node_proxy_agent_v5-2.0.23-py3-none-any.whl", hash = "sha256:36b7cf42b0b940681c6e1c651f09332fbd779906985dc22828e7334efdff9216"},
+ {file = "aws-cdk.asset-node-proxy-agent-v5-2.0.38.tar.gz", hash = "sha256:eb80e0098899bd29e4f0938c68802cc022a8e39af8c05de4053f9a209e63bb7c"},
+ {file = "aws_cdk.asset_node_proxy_agent_v5-2.0.38-py3-none-any.whl", hash = "sha256:0add1debe24e566b8d2cf713ca85eb5e82c42de00f737dfbf02e18e22220a41b"},
]
aws-cdk-aws-apigatewayv2-alpha = [
- {file = "aws-cdk.aws-apigatewayv2-alpha-2.51.1a0.tar.gz", hash = "sha256:d8c1a914bb2b08c0e8999a3cc30e8b8c2bebeb580b253f32b025ee567f02fe3d"},
- {file = "aws_cdk.aws_apigatewayv2_alpha-2.51.1a0-py3-none-any.whl", hash = "sha256:23e14a68769e7fdf4f3c7f9bc56293a14d48b580074a9f327d432aa2ee81362c"},
+ {file = "aws-cdk.aws-apigatewayv2-alpha-2.53.0a0.tar.gz", hash = "sha256:7bfd688d3c22676266ff161012f11ff3f60e11e50ba0d39d18a313ff07f69bbd"},
+ {file = "aws_cdk.aws_apigatewayv2_alpha-2.53.0a0-py3-none-any.whl", hash = "sha256:6864d15ea12c903ae6ca679aaec49dd6c65fc1b537cd317c62cd334f7a382683"},
]
aws-cdk-aws-apigatewayv2-integrations-alpha = [
- {file = "aws-cdk.aws-apigatewayv2-integrations-alpha-2.51.1a0.tar.gz", hash = "sha256:ab7e5d4c7a0037f82115b51609aae283617f3692db41e4b3cc66051ef9f4cd3b"},
- {file = "aws_cdk.aws_apigatewayv2_integrations_alpha-2.51.1a0-py3-none-any.whl", hash = "sha256:ee6c7bb37afef99d3b56a8800f341fe521537406c1e69e46a1aaee9edbd6f6a8"},
+ {file = "aws-cdk.aws-apigatewayv2-integrations-alpha-2.53.0a0.tar.gz", hash = "sha256:fb8bf5812908787a776a9dc5a13bbacec0b39f07517f9a57ebba33d9585ab131"},
+ {file = "aws_cdk.aws_apigatewayv2_integrations_alpha-2.53.0a0-py3-none-any.whl", hash = "sha256:4419283b9a0f41c0cfc5e4a8ba0b0236ca948d874c1c165f058c2c1677ffe6d1"},
]
aws-cdk-lib = [
- {file = "aws-cdk-lib-2.51.1.tar.gz", hash = "sha256:35b66c2ed34490470d1917ee61011fbe053ad22cd1521ee34128f2db78bd2a8a"},
- {file = "aws_cdk_lib-2.51.1-py3-none-any.whl", hash = "sha256:721f5477bad042162f5257fd70fae62a1c0fe499b6cbb71c1f27ea17ba988e96"},
+ {file = "aws-cdk-lib-2.55.1.tar.gz", hash = "sha256:b9a4d7696af52bd42209c16fe94b2721cbb339fae36de56a035d884178d59ce7"},
+ {file = "aws_cdk_lib-2.55.1-py3-none-any.whl", hash = "sha256:93c06ba840e580ff07ce9c15534f8161946b0cd90442f867c8bf0ea223710458"},
]
aws-sam-translator = [
- {file = "aws-sam-translator-1.54.0.tar.gz", hash = "sha256:a3ae79f1f2d430f5ade4d245165d5612414233f540b471d170f1aab95c3713a6"},
- {file = "aws_sam_translator-1.54.0-py2-none-any.whl", hash = "sha256:1bb4abb197e6de3f935425e65f67d14f47eb620d984e9de963b666cc9deb66e4"},
- {file = "aws_sam_translator-1.54.0-py3-none-any.whl", hash = "sha256:10d6771ebbe9107a0ddb756ccffd68ba81d885ef2eace80358a098566e6abaf1"},
+ {file = "aws-sam-translator-1.55.0.tar.gz", hash = "sha256:08e182e76d6fabc13ce2f38b8a3932b3131407c6ad29ec2849ef3d9a41576b94"},
+ {file = "aws_sam_translator-1.55.0-py2-none-any.whl", hash = "sha256:e86a67b87329a0de7d531d33257d1a448d0d6ecd84aee058d084957f28a8e4b1"},
+ {file = "aws_sam_translator-1.55.0-py3-none-any.whl", hash = "sha256:93dc74614ab291c86be681e025679d08f4fa685ed6b55d410f62f2f235012205"},
]
aws-xray-sdk = [
{file = "aws-xray-sdk-2.11.0.tar.gz", hash = "sha256:78835fc841f03e550858f18a9973eab8618f47f22d2f59edf130578fa545a867"},
@@ -1619,20 +1657,20 @@ black = [
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
boto3 = [
- {file = "boto3-1.26.16-py3-none-any.whl", hash = "sha256:4f493a2aed71cee93e626de4f67ce58dd82c0473480a0fc45b131715cd8f4f30"},
- {file = "boto3-1.26.16.tar.gz", hash = "sha256:31c0adf71e4bd19a5428580bb229d7ea3b5795eecaa0847a85385df00c026116"},
+ {file = "boto3-1.26.18-py3-none-any.whl", hash = "sha256:933c88b189112a5fdd82d49ef00f95b9dd649d195e557a81aecb773a3e01c517"},
+ {file = "boto3-1.26.18.tar.gz", hash = "sha256:3c7315da16eb0b41823965e5ce55f99cb07e94680e0ed7830c581f505fb5bd15"},
]
botocore = [
- {file = "botocore-1.29.16-py3-none-any.whl", hash = "sha256:271b599e6cfe214405ed50d41cd967add1d5d469383dd81ff583bc818b47f59b"},
- {file = "botocore-1.29.16.tar.gz", hash = "sha256:8cfcc10f2f1751608c3cec694f2d6b5e16ebcd50d0a104f9914d5616227c62e9"},
+ {file = "botocore-1.29.18-py3-none-any.whl", hash = "sha256:2aba44433b6eac6d3a12cf93f2985e2d7a843307c1a527042fc48dd09b273992"},
+ {file = "botocore-1.29.18.tar.gz", hash = "sha256:26e86fce95049f6cc18b5611901549943c4c22522fa8a3b6b265404f673977b2"},
]
cattrs = [
{file = "cattrs-22.2.0-py3-none-any.whl", hash = "sha256:bc12b1f0d000b9f9bee83335887d532a1d3e99a833d1bf0882151c97d3e68c21"},
{file = "cattrs-22.2.0.tar.gz", hash = "sha256:f0eed5642399423cf656e7b66ce92cdc5b963ecafd041d1b24d136fdde7acf6d"},
]
certifi = [
- {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
- {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
+ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
+ {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
cfn-lint = [
{file = "cfn-lint-0.67.0.tar.gz", hash = "sha256:dfa707e06f4a530ffc9cf66c0af7a4f28b11190b7a6a22536a6c4aa6afc5ff06"},
@@ -1655,8 +1693,8 @@ colorama = [
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
constructs = [
- {file = "constructs-10.1.169-py3-none-any.whl", hash = "sha256:e36cc48c564e9432f76c2e533c3dcdd052167ba34545daab5084a2ce2163f123"},
- {file = "constructs-10.1.169.tar.gz", hash = "sha256:ff4e69bf78affb5a797b0653bcd2b007e375b54d0e5c20f163e37be8fec565d5"},
+ {file = "constructs-10.1.174-py3-none-any.whl", hash = "sha256:6968efd5f837f3a43d5a93808424744cdcd03f53df764be78123f60e81b39b0a"},
+ {file = "constructs-10.1.174.tar.gz", hash = "sha256:af9bd1f0bd6882a6a608bc335a4f8a230f498072bff83d9126d43c486e30305b"},
]
coverage = [
{file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
@@ -1731,20 +1769,22 @@ fastjsonschema = [
{file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"},
]
filelock = [
- {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
- {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
+ {file = "filelock-3.8.2-py3-none-any.whl", hash = "sha256:8df285554452285f79c035efb0c861eb33a4bcfa5b7a137016e32e6a90f9792c"},
+ {file = "filelock-3.8.2.tar.gz", hash = "sha256:7565f628ea56bfcd8e54e42bdc55da899c85c1abfe1b5bcfd147e9188cebb3b2"},
]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
+ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
+ {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
]
flake8-black = [
{file = "flake8-black-0.3.5.tar.gz", hash = "sha256:9e93252b1314a8eb3c2f55dec54a07239e502b12f57567f2c105f2202714b15e"},
{file = "flake8_black-0.3.5-py3-none-any.whl", hash = "sha256:4948a579fdddd98fbf935fd94255dfcfce560c4ddc1ceee08e3f12d6114c8619"},
]
flake8-bugbear = [
- {file = "flake8-bugbear-22.10.27.tar.gz", hash = "sha256:a6708608965c9e0de5fff13904fed82e0ba21ac929fe4896459226a797e11cd5"},
- {file = "flake8_bugbear-22.10.27-py3-none-any.whl", hash = "sha256:6ad0ab754507319060695e2f2be80e6d8977cfcea082293089a9226276bd825d"},
+ {file = "flake8-bugbear-22.12.6.tar.gz", hash = "sha256:4cdb2c06e229971104443ae293e75e64c6107798229202fbe4f4091427a30ac0"},
+ {file = "flake8_bugbear-22.12.6-py3-none-any.whl", hash = "sha256:b69a510634f8a9c298dfda2b18a8036455e6b19ecac4fe582e4d7a0abfa50a30"},
]
flake8-builtins = [
{file = "flake8-builtins-2.0.1.tar.gz", hash = "sha256:5aeb420130efe8acbdaf8708a582492413293a3ca25653518f687937879650a5"},
@@ -1854,16 +1894,16 @@ ijson = [
{file = "ijson-3.1.4.tar.gz", hash = "sha256:1d1003ae3c6115ec9b587d29dd136860a81a23c7626b682e2b5b12c9fd30e4ea"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"},
- {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"},
+ {file = "importlib_metadata-5.1.0-py3-none-any.whl", hash = "sha256:d84d17e21670ec07990e1044a99efe8d615d860fd176fc29ef5c306068fda313"},
+ {file = "importlib_metadata-5.1.0.tar.gz", hash = "sha256:d5059f9f1e8e41f80e9c56c2ee58811450c31984dfa625329ffd7c0dad88a73b"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
isort = [
- {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
- {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
+ {file = "isort-5.11.3-py3-none-any.whl", hash = "sha256:83155ffa936239d986b0f190347a3f2285f42a9b9e1725c89d865b27dd0627e5"},
+ {file = "isort-5.11.3.tar.gz", hash = "sha256:a8ca25fbfad0f7d5d8447a4314837298d9f6b23aed8618584c894574f626b64b"},
]
jinja2 = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
@@ -1878,8 +1918,8 @@ jschema-to-python = [
{file = "jschema_to_python-1.2.3.tar.gz", hash = "sha256:76ff14fe5d304708ccad1284e4b11f96a658949a31ee7faed9e0995279549b91"},
]
jsii = [
- {file = "jsii-1.71.0-py3-none-any.whl", hash = "sha256:0f081498d9f2a12850577caedebe604e4b6e98e7fb20c3bbcca05a5ab8bd0f0b"},
- {file = "jsii-1.71.0.tar.gz", hash = "sha256:e8fedaf077c192917f1d43100b2bbb5d107e4393b713f8ca2814d7653d64a026"},
+ {file = "jsii-1.72.0-py3-none-any.whl", hash = "sha256:4dcf65eca9400c15a6a7c9d85b27f4bfe15c96ad3e36272a502f391556858151"},
+ {file = "jsii-1.72.0.tar.gz", hash = "sha256:6daf1c17362bd07c50c299e08d9a4454075550eb78e035a160ecf9ea68ded3cf"},
]
jsonpatch = [
{file = "jsonpatch-1.32-py2.py3-none-any.whl", hash = "sha256:26ac385719ac9f54df8a2f0827bb8253aa3ea8ab7b3368457bcdb8c14595a397"},
@@ -1957,6 +1997,8 @@ markupsafe = [
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
mergedeep = [
{file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
@@ -1974,8 +2016,8 @@ mkdocs-git-revision-date-plugin = [
{file = "mkdocs_git_revision_date_plugin-0.3.2-py3-none-any.whl", hash = "sha256:2e67956cb01823dd2418e2833f3623dee8604cdf223bddd005fe36226a56f6ef"},
]
mkdocs-material = [
- {file = "mkdocs_material-8.5.10-py3-none-any.whl", hash = "sha256:51760fa4c9ee3ca0b3a661ec9f9817ec312961bb84ff19e5b523fdc5256e1d6c"},
- {file = "mkdocs_material-8.5.10.tar.gz", hash = "sha256:7623608f746c6d9ff68a8ef01f13eddf32fa2cae5e15badb251f26d1196bc8f1"},
+ {file = "mkdocs_material-8.5.11-py3-none-any.whl", hash = "sha256:c907b4b052240a5778074a30a78f31a1f8ff82d7012356dc26898b97559f082e"},
+ {file = "mkdocs_material-8.5.11.tar.gz", hash = "sha256:b0ea0513fd8cab323e8a825d6692ea07fa83e917bb5db042e523afecc7064ab7"},
]
mkdocs-material-extensions = [
{file = "mkdocs_material_extensions-1.1.1-py3-none-any.whl", hash = "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"},
@@ -2020,20 +2062,20 @@ mypy-boto3-cloudformation = [
{file = "mypy_boto3_cloudformation-1.26.11.post1-py3-none-any.whl", hash = "sha256:6182d6950fe06c869be2d9ba49e99a0d71bd3761155f3cacd21004a359ffd5c9"},
]
mypy-boto3-cloudwatch = [
- {file = "mypy-boto3-cloudwatch-1.26.0.post1.tar.gz", hash = "sha256:4798903afa6eb0b1a4a24f2c84ff64bd00810e2e59928e00cfc240cf790aaa1f"},
- {file = "mypy_boto3_cloudwatch-1.26.0.post1-py3-none-any.whl", hash = "sha256:aeef90abeed13d6bddf7f878ec68a78903347fbd53022ad1133ef86b6a5dabc4"},
+ {file = "mypy-boto3-cloudwatch-1.26.30.tar.gz", hash = "sha256:2d24b37a6476e19e5e3bba97590b23b33522c418f8963aba5773b2e9c922c6df"},
+ {file = "mypy_boto3_cloudwatch-1.26.30-py3-none-any.whl", hash = "sha256:9d96012f3a55ff87ede8645b24a725b56ee8caaa37a7e15772b7d786d73446b8"},
]
mypy-boto3-dynamodb = [
- {file = "mypy-boto3-dynamodb-1.26.13.post16.tar.gz", hash = "sha256:508df8bb7a637f7c5a57744f8c80de6d0dfb3823c8449de8ef565e798b371a04"},
- {file = "mypy_boto3_dynamodb-1.26.13.post16-py3-none-any.whl", hash = "sha256:76940c8aeb61945d6be3b78322825503d27d6481036d4c4498f02bf15a3de765"},
+ {file = "mypy-boto3-dynamodb-1.26.24.tar.gz", hash = "sha256:94d059f692189fa3f1315df4e16f02b94018b09b61e3a54bd56c9715f5bfe418"},
+ {file = "mypy_boto3_dynamodb-1.26.24-py3-none-any.whl", hash = "sha256:41fc3303eb9b7153e2726f948a952cf7a1344cad5e44e0c225e3c6c3decfe2a6"},
]
mypy-boto3-lambda = [
- {file = "mypy-boto3-lambda-1.26.12.tar.gz", hash = "sha256:2063fa9c9f926e686a0d6229f063be3e5a083729f2f4de9c547073970a476ce3"},
- {file = "mypy_boto3_lambda-1.26.12-py3-none-any.whl", hash = "sha256:8c9391f51f9ec0d2e6b472a9e465d17605b704535a9430017e2d2ace7d6679c5"},
+ {file = "mypy-boto3-lambda-1.26.18.tar.gz", hash = "sha256:ad36a98e4bd7c95eb6bf750bc63932367cbed8bbe79bca1fdb7e753e2a689a8b"},
+ {file = "mypy_boto3_lambda-1.26.18-py3-none-any.whl", hash = "sha256:8514bf21fe3158c3f555906c2575403b3bbbc3891b3cff5869ec75a7fa8477ce"},
]
mypy-boto3-logs = [
- {file = "mypy-boto3-logs-1.26.3.tar.gz", hash = "sha256:45f6616118ef758eb899fff206f4c835b443845af5d77b16f39c19c1a257dcec"},
- {file = "mypy_boto3_logs-1.26.3-py3-none-any.whl", hash = "sha256:676355cc513a3a38ae83ba7d69a7aa62ce45522e298d510cbbb3011045be6931"},
+ {file = "mypy-boto3-logs-1.26.27.tar.gz", hash = "sha256:f460c59ec8c47e7a651e185567681781283a3d1d7302637d7ba211a9b888726b"},
+ {file = "mypy_boto3_logs-1.26.27-py3-none-any.whl", hash = "sha256:a0d9b26c8590de0996966bab48d01a4dc5df8ec3a8618994c0bfefba409d5654"},
]
mypy-boto3-s3 = [
{file = "mypy-boto3-s3-1.26.0.post1.tar.gz", hash = "sha256:6d7079f8c739dc993cbedad0736299c413b297814b73795a3855a79169ecc938"},
@@ -2098,6 +2140,8 @@ py-cpuinfo = [
pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
+ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
+ {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
]
pydantic = [
{file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
@@ -2140,6 +2184,8 @@ pydantic = [
pyflakes = [
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
+ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
+ {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
]
pygments = [
{file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
@@ -2182,8 +2228,8 @@ pytest = [
{file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
]
pytest-asyncio = [
- {file = "pytest-asyncio-0.20.2.tar.gz", hash = "sha256:32a87a9836298a881c0ec637ebcc952cfe23a56436bdc0d09d1511941dd8a812"},
- {file = "pytest_asyncio-0.20.2-py3-none-any.whl", hash = "sha256:07e0abf9e6e6b95894a39f688a4a875d63c2128f76c02d03d16ccbc35bcc0f8a"},
+ {file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"},
+ {file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"},
]
pytest-benchmark = [
{file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"},
@@ -2198,8 +2244,8 @@ pytest-mock = [
{file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"},
]
pytest-xdist = [
- {file = "pytest-xdist-3.0.2.tar.gz", hash = "sha256:688da9b814370e891ba5de650c9327d1a9d861721a524eb917e620eec3e90291"},
- {file = "pytest_xdist-3.0.2-py3-none-any.whl", hash = "sha256:9feb9a18e1790696ea23e1434fa73b325ed4998b0e9fcb221f16fd1945e6df1b"},
+ {file = "pytest-xdist-3.1.0.tar.gz", hash = "sha256:40fdb8f3544921c5dfcd486ac080ce22870e71d82ced6d2e78fa97c2addd480c"},
+ {file = "pytest_xdist-3.1.0-py3-none-any.whl", hash = "sha256:70a76f191d8a1d2d6be69fc440cdf85f3e4c03c08b520fd5dc5d338d6cf07d89"},
]
python-dateutil = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
@@ -2489,6 +2535,6 @@ xenon = [
{file = "xenon-0.9.0.tar.gz", hash = "sha256:d2b9cb6c6260f771a432c1e588e51fddb17858f88f73ef641e7532f7a5f58fb8"},
]
zipp = [
- {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"},
- {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"},
+ {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
+ {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
]
diff --git a/pyproject.toml b/pyproject.toml
index b6f33f92cc7..3f3180a9cb4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aws_lambda_powertools"
-version = "2.4.0"
+version = "2.5.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"]
@@ -31,49 +31,55 @@ coverage = {extras = ["toml"], version = "^6.2"}
pytest = "^7.0.1"
black = "^22.8"
boto3 = "^1.18"
+flake8 = [
+ # https://github.com/python/importlib_metadata/issues/406
+ { version = "*", python="^3.7" },
+ { version = ">=5", python= ">=3.8"},
+]
flake8-builtins = "^2.0.1"
flake8-comprehensions = "^3.10.1"
flake8-debugger = "^4.0.0"
flake8-fixme = "^1.1.1"
flake8-variables-names = "^0.0.5"
-flake8-black = "^0.3.3"
-isort = "^5.10.1"
+flake8-black = "^0.3.5"
+isort = "^5.11.3"
pytest-cov = "^4.0.0"
pytest-mock = "^3.5.1"
pdoc3 = "^0.10.0"
-pytest-asyncio = "^0.20.2"
+pytest-asyncio = "^0.20.3"
bandit = "^1.7.1"
radon = "^5.1.0"
xenon = "^0.9.0"
flake8-eradicate = "^1.2.1"
-flake8-bugbear = "^22.10.27"
+flake8-bugbear = "^22.12.6"
mkdocs-git-revision-date-plugin = "^0.3.2"
mike = "^1.1.2"
retry = "^0.9.2"
-pytest-xdist = "^3.0.2"
-aws-cdk-lib = "^2.51.1"
+pytest-xdist = "^3.1.0"
+aws-cdk-lib = "^2.55.1"
"aws-cdk.aws-apigatewayv2-alpha" = "^2.38.1-alpha.0"
"aws-cdk.aws-apigatewayv2-integrations-alpha" = "^2.38.1-alpha.0"
pytest-benchmark = "^4.0.0"
python-snappy = "^0.6.1"
mypy-boto3-appconfig = "^1.26.0"
mypy-boto3-cloudformation = "^1.26.11"
-mypy-boto3-cloudwatch = "^1.26.0"
-mypy-boto3-dynamodb = "^1.26.13"
+mypy-boto3-cloudwatch = "^1.26.30"
+mypy-boto3-dynamodb = "^1.26.24"
mypy-boto3-lambda = "^1.26.12"
-mypy-boto3-logs = "^1.26.3"
+mypy-boto3-logs = "^1.26.27"
mypy-boto3-secretsmanager = "^1.26.12"
mypy-boto3-ssm = "^1.26.11"
mypy-boto3-s3 = "^1.26.0"
mypy-boto3-xray = "^1.26.11"
types-requests = "^2.28.11"
typing-extensions = "^4.4.0"
-mkdocs-material = "^8.5.10"
-filelock = "^3.8.0"
+mkdocs-material = "^8.5.11"
+filelock = "^3.8.2"
checksumdir = "^1.2.0"
mypy-boto3-appconfigdata = "^1.26.0"
-importlib-metadata = "^4.13"
+importlib-metadata = "^5.1"
ijson = "^3.1.4"
+typed-ast = { version = "^1.5.4", python = "< 3.8"}
[tool.poetry.extras]
parser = ["pydantic"]
diff --git a/tests/functional/test_headers_serializer.py b/tests/functional/test_headers_serializer.py
index 8a27ce8baa8..204df5ad614 100644
--- a/tests/functional/test_headers_serializer.py
+++ b/tests/functional/test_headers_serializer.py
@@ -145,3 +145,21 @@ def test_single_value_headers_with_multiple_header_values_warning():
payload = serializer.serialize(cookies=[], headers=headers)
assert payload["headers"]["Foo"] == headers["Foo"][-1]
+
+
+def test_http_api_headers_serializer_with_null_values():
+ serializer = HttpApiHeadersSerializer()
+ payload = serializer.serialize(headers={"Foo": None}, cookies=[])
+ assert payload == {"headers": {}, "cookies": []}
+
+
+def test_multi_value_headers_serializer_with_null_values():
+ serializer = MultiValueHeadersSerializer()
+ payload = serializer.serialize(headers={"Foo": None}, cookies=[])
+ assert payload == {"multiValueHeaders": {}}
+
+
+def test_single_value_headers_serializer_with_null_values():
+ serializer = SingleValueHeadersSerializer()
+ payload = serializer.serialize(headers={"Foo": None}, cookies=[])
+ assert payload["headers"] == {}
diff --git a/tests/functional/test_shared_functions.py b/tests/unit/test_shared_functions.py
similarity index 68%
rename from tests/functional/test_shared_functions.py
rename to tests/unit/test_shared_functions.py
index a8e1755bdd5..00abe2c6e08 100644
--- a/tests/functional/test_shared_functions.py
+++ b/tests/unit/test_shared_functions.py
@@ -1,15 +1,19 @@
import warnings
+from dataclasses import dataclass
import pytest
+from pydantic import BaseModel
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.shared.functions import (
+ extract_event_from_common_models,
powertools_debug_is_set,
powertools_dev_is_set,
resolve_env_var_choice,
resolve_truthy_env_var_choice,
strtobool,
)
+from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
def test_resolve_env_var_choice_explicit_wins_over_env_var():
@@ -64,3 +68,38 @@ def test_powertools_debug_warning(monkeypatch: pytest.MonkeyPatch):
powertools_debug_is_set()
assert len(w) == 1
assert str(w[0].message) == warning_message
+
+
+def test_extract_event_dict():
+ data = {"hello": "world"}
+ assert extract_event_from_common_models(data) == data
+
+
+def test_extract_event_pydantic():
+ class DummyModel(BaseModel):
+ hello: str
+
+ data = {"hello": "world"}
+ assert extract_event_from_common_models(DummyModel(**data)) == data
+
+
+def test_extract_event_dict_wrapper():
+ class DummyClassSample(DictWrapper):
+ pass
+
+ data = {"hello": "world"}
+ assert extract_event_from_common_models(DummyClassSample(data)) == data
+
+
+def test_extract_event_dataclass():
+ @dataclass
+ class DummyDataclass:
+ hello: str
+
+ data = {"hello": "world"}
+ assert extract_event_from_common_models(DummyDataclass(**data)) == data
+
+
+@pytest.mark.parametrize("data", [False, True, "", 10, [], {}, object])
+def test_extract_event_any(data):
+ assert extract_event_from_common_models(data) == data