diff --git a/.github/actions/verify-provenance/verify_provenance.sh b/.github/actions/verify-provenance/verify_provenance.sh
index ca8bf57c6be..dbfe280473d 100755
--- a/.github/actions/verify-provenance/verify_provenance.sh
+++ b/.github/actions/verify-provenance/verify_provenance.sh
@@ -36,35 +36,58 @@ export readonly FILES=("${SLSA_VERIFIER_BINARY}" "${SLSA_VERIFIER_CHECKSUM_FILE}
function debug() {
TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z
- echo ""${TIMESTAMP}" DEBUG - $1"
+ echo ""${TIMESTAMP}" DEBUG - [*] $1"
}
-function download_slsa_verifier() {
- debug "[*] Downloading SLSA Verifier for - Binary: slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
- curl --location --silent -O "https://github.com/slsa-framework/slsa-verifier/releases/download/v${SLSA_VERIFIER_VERSION}/slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
-
- debug "[*] Downloading SLSA Verifier checksums"
- curl --location --silent -O "https://raw.githubusercontent.com/slsa-framework/slsa-verifier/f59b55ef2190581d40fc1a5f3b7a51cab2f4a652/${SLSA_VERIFIER_CHECKSUM_FILE}"
+function error() {
+ cleanup
+ TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z
+ echo ""${TIMESTAMP}" ERROR - [!] $1"
+ echo ""${TIMESTAMP}" ERROR - [!] exiting"
+ exit 1
+}
- debug "[*] Verifying SLSA Verifier binary integrity"
+function download_slsa_verifier() {
+ readonly SLSA_URL="https://github.com/slsa-framework/slsa-verifier/releases/download/v${SLSA_VERIFIER_VERSION}/slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
+ # debug "Downloading SLSA Verifier for - Binary: slsa-verifier-${OS_NAME}-${ARCHITECTURE}"
+ debug "Downloading SLSA Verifier binary: ${SLSA_URL}"
+ curl \
+ --location \
+ --fail \
+ --silent \
+ -O "${SLSA_URL}" || error "Failed to download SLSA Verifier binary"
+
+ readonly SLSA_CHECKSUM_URL="https://raw.githubusercontent.com/slsa-framework/slsa-verifier/f59b55ef2190581d40fc1a5f3b7a51cab2f4a652/${SLSA_VERIFIER_CHECKSUM_FILE}"
+ debug "Downloading SLSA Verifier checksums"
+ curl \
+ --location \
+ --fail \
+ --silent \
+ -O "${SLSA_CHECKSUM_URL}" || error "Failed to download SLSA Verifier binary checksum file"
+
+ debug "Verifying SLSA Verifier binary integrity"
CURRENT_HASH=$(sha256sum "${SLSA_VERIFIER_BINARY}" | awk '{print $1}')
if [[ $(grep "${CURRENT_HASH}" "${SLSA_VERIFIER_CHECKSUM_FILE}") ]]; then
- debug "[*] SLSA Verifier binary integrity confirmed"
+ debug "SLSA Verifier binary integrity confirmed"
chmod +x "${SLSA_VERIFIER_BINARY}"
else
- debug "[!] Failed integrity check for SLSA Verifier binary: ${SLSA_VERIFIER_BINARY}"
- exit 1
+ error "Failed integrity check for SLSA Verifier binary: ${SLSA_VERIFIER_BINARY}"
fi
}
function download_provenance() {
- debug "[*] Downloading attestation for - Release: https://github.com/${ORG}/${REPO}/releases/v${RELEASE_VERSION}"
-
- curl --location --silent -O "https://github.com/${ORG}/${REPO}/releases/download/v${RELEASE_VERSION}/${PROVENANCE_FILE}"
+ readonly PROVENANCE_URL="https://github.com/${ORG}/${REPO}/releases/download/v${RELEASE_VERSION}/${PROVENANCE_FILE}"
+ debug "Downloading attestation: ${PROVENANCE_URL}"
+
+ curl \
+ --location \
+ --fail \
+ --silent \
+ -O ${PROVENANCE_URL} || error "Failed to download provenance. Does the release already exist?"
}
function download_release_artifact() {
- debug "[*] Downloading ${RELEASE_VERSION} release from PyPi"
+ debug "Downloading ${RELEASE_VERSION} release from PyPi"
python -m pip download \
--only-binary=:all: \
--no-deps \
@@ -73,7 +96,7 @@ function download_release_artifact() {
}
function verify_provenance() {
- debug "[*] Verifying attestation with slsa-verifier"
+ debug "Verifying attestation with slsa-verifier"
"${SLSA_VERIFIER_BINARY}" verify-artifact \
--provenance-path "${PROVENANCE_FILE}" \
--source-uri github.com/${ORG}/${REPO} \
@@ -81,11 +104,11 @@ function verify_provenance() {
}
function cleanup() {
- debug "[*] Cleaning up previously downloaded files"
- rm "${SLSA_VERIFIER_BINARY}"
- rm "${SLSA_VERIFIER_CHECKSUM_FILE}"
- rm "${PROVENANCE_FILE}"
- rm "${RELEASE_BINARY}"
+ debug "Cleaning up previously downloaded files"
+ rm -f "${SLSA_VERIFIER_BINARY}"
+ rm -f "${SLSA_VERIFIER_CHECKSUM_FILE}"
+ rm -f "${PROVENANCE_FILE}"
+ rm -f "${RELEASE_BINARY}"
echo "${FILES[@]}" | xargs -n1 echo "Removed file: "
}
diff --git a/.github/workflows/quality_check_pydanticv2.yml b/.github/workflows/quality_check_pydanticv2.yml
new file mode 100644
index 00000000000..435ee5df868
--- /dev/null
+++ b/.github/workflows/quality_check_pydanticv2.yml
@@ -0,0 +1,76 @@
+name: Code quality - Pydanticv2
+
+# PROCESS
+#
+# 1. Install all dependencies and spin off containers for all supported Python versions
+# 2. Run code formatters and linters (various checks) for code standard
+# 3. Run static typing checker for potential bugs
+# 4. Run entire test suite for regressions except end-to-end (unit, functional, performance)
+# 5. Run static analysis (in addition to CodeQL) for common insecure code practices
+# 6. Run complexity baseline to avoid error-prone bugs and keep maintenance lower
+# 7. Collect and report on test coverage
+
+# USAGE
+#
+# Always triggered on new PRs, PR changes and PR merge.
+
+
+on:
+ pull_request:
+ paths:
+ - "aws_lambda_powertools/**"
+ - "tests/**"
+ - "pyproject.toml"
+ - "poetry.lock"
+ - "mypy.ini"
+ branches:
+ - develop
+ push:
+ paths:
+ - "aws_lambda_powertools/**"
+ - "tests/**"
+ - "pyproject.toml"
+ - "poetry.lock"
+ - "mypy.ini"
+ branches:
+ - develop
+
+permissions:
+ contents: read
+
+jobs:
+ quality_check:
+ runs-on: ubuntu-latest
+ strategy:
+ max-parallel: 4
+ matrix:
+ python-version: ["3.7", "3.8", "3.9", "3.10"]
+ env:
+ PYTHON: "${{ matrix.python-version }}"
+ permissions:
+ contents: read # checkout code only
+ steps:
+ - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
+ - name: Install poetry
+ run: pipx install poetry
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0 # v4.6.1
+ with:
+ python-version: ${{ matrix.python-version }}
+ cache: "poetry"
+ - name: Removing dev dependencies locked to Pydantic v1
+ run: poetry remove cfn-lint
+ - name: Replacing Pydantic v1 with v2 > 2.0.3
+ run: poetry add "pydantic=^2.0.3"
+ - name: Install dependencies
+ run: make dev
+ - name: Formatting and Linting
+ run: make lint
+ - name: Static type checking
+ run: make mypy
+ - name: Test with pytest
+ run: make test
+ - name: Security baseline
+ run: make security-baseline
+ - name: Complexity baseline
+ run: make complexity-baseline
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a5ee05b4f1..1d54c049272 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,80 +4,13 @@
# Unreleased
-## Bug Fixes
-
-* **docs:** ensure alias is applied to versioned releases ([#2644](https://github.com/aws-powertools/powertools-lambda-python/issues/2644))
-* **docs:** ensure version alias is in an array to prevent "you're not viewing the latest version" incorrect message ([#2629](https://github.com/aws-powertools/powertools-lambda-python/issues/2629))
-* **logger:** ensure logs stream to stdout by default, not stderr ([#2736](https://github.com/aws-powertools/powertools-lambda-python/issues/2736))
-
-## Code Refactoring
-
-* **parser:** convert functional tests to unit tests ([#2656](https://github.com/aws-powertools/powertools-lambda-python/issues/2656))
-## Documentation
-
-* **batch:** fix custom batch processor example ([#2714](https://github.com/aws-powertools/powertools-lambda-python/issues/2714))
-* **contributing:** add code integration journey graph ([#2685](https://github.com/aws-powertools/powertools-lambda-python/issues/2685))
-* **maintainers:** add cicd pipeline diagram ([#2692](https://github.com/aws-powertools/powertools-lambda-python/issues/2692))
-* **process:** explain our integration automated checks; revamp navigation ([#2764](https://github.com/aws-powertools/powertools-lambda-python/issues/2764))
-
-## Features
+
+## [v2.20.0] - 2023-07-14
+## Maintenance
-* **metrics:** support to set default dimension in EphemeralMetrics ([#2748](https://github.com/aws-powertools/powertools-lambda-python/issues/2748))
-
-## Maintenance
-
-* **ci:** use deps sha for docs and gitpod images based on ossf findings ([#2662](https://github.com/aws-powertools/powertools-lambda-python/issues/2662))
-* **ci:** prevent merging PRs that do not meet minimum requirements ([#2639](https://github.com/aws-powertools/powertools-lambda-python/issues/2639))
-* **ci:** enforce pip --require-hashes to maybe satistify scorecard ([#2679](https://github.com/aws-powertools/powertools-lambda-python/issues/2679))
-* **ci:** enforce top-level permission to minimum fail-safe permission as per openssf ([#2638](https://github.com/aws-powertools/powertools-lambda-python/issues/2638))
-* **ci:** introduce provenance and attestation in release ([#2746](https://github.com/aws-powertools/powertools-lambda-python/issues/2746))
-* **ci:** propagate checkout permission to nested workflows ([#2642](https://github.com/aws-powertools/powertools-lambda-python/issues/2642))
-* **ci:** improves dependabot based on ossf scorecard recommendations ([#2647](https://github.com/aws-powertools/powertools-lambda-python/issues/2647))
-* **ci:** add gitleaks in pre-commit hooks as an extra safety measure ([#2677](https://github.com/aws-powertools/powertools-lambda-python/issues/2677))
-* **ci:** prevent sast codeql to run in forks ([#2711](https://github.com/aws-powertools/powertools-lambda-python/issues/2711))
-* **ci:** use sast on every commit on any supported language ([#2646](https://github.com/aws-powertools/powertools-lambda-python/issues/2646))
-* **ci:** address ossf scorecard findings on npm, pip, and top-level permission leftover ([#2694](https://github.com/aws-powertools/powertools-lambda-python/issues/2694))
-* **deps:** bump github.com/aws/aws-sdk-go-v2/config from 1.17.8 to 1.18.27 in /layer/scripts/layer-balancer ([#2651](https://github.com/aws-powertools/powertools-lambda-python/issues/2651))
-* **deps:** bump docker/setup-buildx-action from 2.8.0 to 2.9.0 ([#2718](https://github.com/aws-powertools/powertools-lambda-python/issues/2718))
-* **deps:** bump github.com/aws/aws-sdk-go-v2/service/lambda from 1.24.6 to 1.37.0 in /layer/scripts/layer-balancer ([#2653](https://github.com/aws-powertools/powertools-lambda-python/issues/2653))
-* **deps:** bump golang.org/x/sync from 0.1.0 to 0.3.0 in /layer/scripts/layer-balancer ([#2649](https://github.com/aws-powertools/powertools-lambda-python/issues/2649))
-* **deps:** bump actions/dependency-review-action from 2.5.1 to 3.0.6 ([#2650](https://github.com/aws-powertools/powertools-lambda-python/issues/2650))
-* **deps:** bump pydantic from 1.10.9 to 1.10.10 ([#2624](https://github.com/aws-powertools/powertools-lambda-python/issues/2624))
-* **deps:** bump pydantic from 1.10.10 to 1.10.11 ([#2671](https://github.com/aws-powertools/powertools-lambda-python/issues/2671))
-* **deps:** migrate from retry to retry2 to address CVE-2022-42969 ([#2665](https://github.com/aws-powertools/powertools-lambda-python/issues/2665))
-* **deps:** bump squidfunk/mkdocs-material from `3837c0f` to `a28ed81` in /docs ([#2669](https://github.com/aws-powertools/powertools-lambda-python/issues/2669))
-* **deps:** bump zgosalvez/github-actions-ensure-sha-pinned-actions from 2.1.3 to 2.1.4 ([#2738](https://github.com/aws-powertools/powertools-lambda-python/issues/2738))
-* **deps:** bump github.com/aws/aws-sdk-go-v2 from 1.16.16 to 1.18.1 in /layer/scripts/layer-balancer ([#2654](https://github.com/aws-powertools/powertools-lambda-python/issues/2654))
-* **deps:** bump docker/setup-buildx-action from 2.9.0 to 2.9.1 ([#2755](https://github.com/aws-powertools/powertools-lambda-python/issues/2755))
-* **deps:** bump pypa/gh-action-pypi-publish from 1.8.7 to 1.8.8 ([#2754](https://github.com/aws-powertools/powertools-lambda-python/issues/2754))
-* **deps:** bump actions/setup-node from 3.6.0 to 3.7.0 ([#2689](https://github.com/aws-powertools/powertools-lambda-python/issues/2689))
-* **deps-dev:** bump mypy-boto3-cloudwatch from 1.27.0 to 1.28.0 ([#2697](https://github.com/aws-powertools/powertools-lambda-python/issues/2697))
-* **deps-dev:** bump ruff from 0.0.276 to 0.0.277 ([#2682](https://github.com/aws-powertools/powertools-lambda-python/issues/2682))
-* **deps-dev:** bump mypy-boto3-cloudformation from 1.27.0 to 1.28.0 ([#2700](https://github.com/aws-powertools/powertools-lambda-python/issues/2700))
-* **deps-dev:** bump mypy-boto3-appconfigdata from 1.27.0 to 1.28.0 ([#2699](https://github.com/aws-powertools/powertools-lambda-python/issues/2699))
-* **deps-dev:** bump sentry-sdk from 1.27.0 to 1.27.1 ([#2701](https://github.com/aws-powertools/powertools-lambda-python/issues/2701))
-* **deps-dev:** bump typed-ast from 1.5.4 to 1.5.5 ([#2670](https://github.com/aws-powertools/powertools-lambda-python/issues/2670))
-* **deps-dev:** bump mypy-boto3-lambda from 1.27.0 to 1.28.0 ([#2698](https://github.com/aws-powertools/powertools-lambda-python/issues/2698))
-* **deps-dev:** bump ruff from 0.0.275 to 0.0.276 ([#2655](https://github.com/aws-powertools/powertools-lambda-python/issues/2655))
-* **deps-dev:** bump sentry-sdk from 1.26.0 to 1.27.0 ([#2652](https://github.com/aws-powertools/powertools-lambda-python/issues/2652))
-* **deps-dev:** bump aws-cdk from 2.86.0 to 2.87.0 ([#2696](https://github.com/aws-powertools/powertools-lambda-python/issues/2696))
-* **deps-dev:** bump mypy-boto3-dynamodb from 1.26.158 to 1.26.164 ([#2622](https://github.com/aws-powertools/powertools-lambda-python/issues/2622))
-* **deps-dev:** bump mypy-boto3-appconfig from 1.27.0 to 1.28.0 ([#2722](https://github.com/aws-powertools/powertools-lambda-python/issues/2722))
-* **deps-dev:** bump mypy-boto3-s3 from 1.27.0 to 1.28.0 ([#2721](https://github.com/aws-powertools/powertools-lambda-python/issues/2721))
-* **deps-dev:** bump mypy-boto3-logs from 1.27.0 to 1.28.1 ([#2723](https://github.com/aws-powertools/powertools-lambda-python/issues/2723))
-* **deps-dev:** bump mypy-boto3-ssm from 1.27.0 to 1.28.0 ([#2724](https://github.com/aws-powertools/powertools-lambda-python/issues/2724))
-* **deps-dev:** bump mypy-boto3-xray from 1.27.0 to 1.28.0 ([#2720](https://github.com/aws-powertools/powertools-lambda-python/issues/2720))
-* **deps-dev:** bump mypy-boto3-dynamodb from 1.27.0 to 1.28.0 ([#2740](https://github.com/aws-powertools/powertools-lambda-python/issues/2740))
-* **deps-dev:** bump mypy-boto3-secretsmanager from 1.27.0 to 1.28.0 ([#2739](https://github.com/aws-powertools/powertools-lambda-python/issues/2739))
-* **deps-dev:** bump sentry-sdk from 1.27.1 to 1.28.0 ([#2741](https://github.com/aws-powertools/powertools-lambda-python/issues/2741))
-* **deps-dev:** bump ruff from 0.0.277 to 0.0.278 ([#2758](https://github.com/aws-powertools/powertools-lambda-python/issues/2758))
-* **deps-dev:** bump pytest-asyncio from 0.21.0 to 0.21.1 ([#2756](https://github.com/aws-powertools/powertools-lambda-python/issues/2756))
-* **deps-dev:** bump mypy-boto3-appconfigdata from 1.26.70 to 1.27.0 ([#2636](https://github.com/aws-powertools/powertools-lambda-python/issues/2636))
-* **deps-dev:** bump cfn-lint from 0.77.10 to 0.78.1 ([#2757](https://github.com/aws-powertools/powertools-lambda-python/issues/2757))
-* **governance:** update active maintainers list ([#2715](https://github.com/aws-powertools/powertools-lambda-python/issues/2715))
-* **streaming:** replace deprecated Version classes from distutils ([#2752](https://github.com/aws-powertools/powertools-lambda-python/issues/2752))
-* **user-agent:** support patching botocore session ([#2614](https://github.com/aws-powertools/powertools-lambda-python/issues/2614))
+* version bump
+* **deps-dev:** bump mypy-boto3-secretsmanager from 1.28.0 to 1.28.3 ([#2773](https://github.com/aws-powertools/powertools-lambda-python/issues/2773))
@@ -3537,7 +3470,8 @@
* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38
-[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.19.0...HEAD
+[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.20.0...HEAD
+[v2.20.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.19.0...v2.20.0
[v2.19.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.18.0...v2.19.0
[v2.18.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.17.0...v2.18.0
[v2.17.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v2.16.2...v2.17.0
diff --git a/README.md b/README.md
index 9ace4a52a2a..8f4b6344d14 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ The following companies, among others, use Powertools:
* [CyberArk](https://www.cyberark.com/)
* [globaldatanet](https://globaldatanet.com/)
* [IMS](https://ims.tech/)
+* [Jit Security](https://www.jit.io/)
* [Propellor.ai](https://www.propellor.ai/)
* [TopSport](https://www.topsport.com.au/)
* [Trek10](https://www.trek10.com/)
diff --git a/aws_lambda_powertools/shared/version.py b/aws_lambda_powertools/shared/version.py
index 24ce0f2fd70..f22bb031e1d 100644
--- a/aws_lambda_powertools/shared/version.py
+++ b/aws_lambda_powertools/shared/version.py
@@ -1,3 +1,3 @@
"""Exposes version constant to avoid circular dependencies."""
-VERSION = "2.19.0"
+VERSION = "2.21.0"
diff --git a/aws_lambda_powertools/utilities/batch/base.py b/aws_lambda_powertools/utilities/batch/base.py
index 4ab2c1a2b0b..b00b31449f2 100644
--- a/aws_lambda_powertools/utilities/batch/base.py
+++ b/aws_lambda_powertools/utilities/batch/base.py
@@ -348,6 +348,11 @@ def _to_batch_type(self, record: dict, event_type: EventType) -> EventSourceData
def _to_batch_type(self, record: dict, event_type: EventType, model: Optional["BatchTypeModels"] = None):
if model is not None:
+ # If a model is provided, we assume Pydantic is installed and we need to disable v2 warnings
+ from aws_lambda_powertools.utilities.parser.compat import disable_pydantic_v2_warning
+
+ disable_pydantic_v2_warning()
+
return model.parse_obj(record)
return self._DATA_CLASS_MAPPING[event_type](record)
@@ -500,8 +505,13 @@ def _process_record(self, record: dict) -> Union[SuccessResponse, FailureRespons
# we need to handle that exception differently.
# We check for a public attr in validation errors coming from Pydantic exceptions (subclass or not)
# and we compare if it's coming from the same model that trigger the exception in the first place
- model = getattr(exc, "model", None)
- if model == self.model:
+
+ # Pydantic v1 raises a ValidationError with ErrorWrappers and store the model instance in a class variable.
+ # Pydantic v2 simplifies this by adding a title variable to store the model name directly.
+ model = getattr(exc, "model", None) or getattr(exc, "title", None)
+ model_name = getattr(self.model, "__name__", None)
+
+ if model == self.model or model == model_name:
return self._register_model_validation_error_record(record)
return self.failure_handler(record=data, exception=sys.exc_info())
@@ -644,8 +654,13 @@ async def _async_process_record(self, record: dict) -> Union[SuccessResponse, Fa
# we need to handle that exception differently.
# We check for a public attr in validation errors coming from Pydantic exceptions (subclass or not)
# and we compare if it's coming from the same model that trigger the exception in the first place
- model = getattr(exc, "model", None)
- if model == self.model:
+
+ # Pydantic v1 raises a ValidationError with ErrorWrappers and store the model instance in a class variable.
+ # Pydantic v2 simplifies this by adding a title variable to store the model name directly.
+ model = getattr(exc, "model", None) or getattr(exc, "title", None)
+ model_name = getattr(self.model, "__name__", None)
+
+ if model == self.model or model == model_name:
return self._register_model_validation_error_record(record)
return self.failure_handler(record=data, exception=sys.exc_info())
diff --git a/aws_lambda_powertools/utilities/parser/compat.py b/aws_lambda_powertools/utilities/parser/compat.py
new file mode 100644
index 00000000000..c73098421b1
--- /dev/null
+++ b/aws_lambda_powertools/utilities/parser/compat.py
@@ -0,0 +1,34 @@
+import functools
+
+
+@functools.lru_cache(maxsize=None)
+def disable_pydantic_v2_warning():
+ """
+ Disables the Pydantic version 2 warning by filtering out the related warnings.
+
+ This function checks the version of Pydantic currently installed and if it is version 2,
+ it filters out the PydanticDeprecationWarning and PydanticDeprecatedSince20 warnings
+ to suppress them.
+
+ Since we only need to run the code once, we are using lru_cache to improve performance.
+
+ Note: This function assumes that Pydantic is installed.
+
+ Usage:
+ disable_pydantic_v2_warning()
+ """
+ try:
+ from pydantic import __version__
+
+ version = __version__.split(".")
+
+ if int(version[0]) == 2:
+ import warnings
+
+ from pydantic import PydanticDeprecatedSince20, PydanticDeprecationWarning
+
+ warnings.filterwarnings("ignore", category=PydanticDeprecationWarning)
+ warnings.filterwarnings("ignore", category=PydanticDeprecatedSince20)
+
+ except ImportError:
+ pass
diff --git a/aws_lambda_powertools/utilities/parser/envelopes/base.py b/aws_lambda_powertools/utilities/parser/envelopes/base.py
index 85486fdd876..101e157ef69 100644
--- a/aws_lambda_powertools/utilities/parser/envelopes/base.py
+++ b/aws_lambda_powertools/utilities/parser/envelopes/base.py
@@ -2,6 +2,7 @@
from abc import ABC, abstractmethod
from typing import Any, Dict, Optional, Type, TypeVar, Union
+from aws_lambda_powertools.utilities.parser.compat import disable_pydantic_v2_warning
from aws_lambda_powertools.utilities.parser.types import Model
logger = logging.getLogger(__name__)
@@ -26,6 +27,8 @@ def _parse(data: Optional[Union[Dict[str, Any], Any]], model: Type[Model]) -> Un
Any
Parsed data
"""
+ disable_pydantic_v2_warning()
+
if data is None:
logger.debug("Skipping parsing as event is None")
return data
diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py
index ddc76dc7819..f1b2d30d9cf 100644
--- a/aws_lambda_powertools/utilities/parser/models/__init__.py
+++ b/aws_lambda_powertools/utilities/parser/models/__init__.py
@@ -1,3 +1,7 @@
+from aws_lambda_powertools.utilities.parser.compat import disable_pydantic_v2_warning
+
+disable_pydantic_v2_warning()
+
from .alb import AlbModel, AlbRequestContext, AlbRequestContextData
from .apigw import (
APIGatewayEventAuthorizer,
diff --git a/aws_lambda_powertools/utilities/parser/models/apigw.py b/aws_lambda_powertools/utilities/parser/models/apigw.py
index 82a3a6188d2..c17b094d0c0 100644
--- a/aws_lambda_powertools/utilities/parser/models/apigw.py
+++ b/aws_lambda_powertools/utilities/parser/models/apigw.py
@@ -21,74 +21,74 @@ class ApiGatewayUserCert(BaseModel):
class APIGatewayEventIdentity(BaseModel):
- accessKey: Optional[str]
- accountId: Optional[str]
- apiKey: Optional[str]
- apiKeyId: Optional[str]
- caller: Optional[str]
- cognitoAuthenticationProvider: Optional[str]
- cognitoAuthenticationType: Optional[str]
- cognitoIdentityId: Optional[str]
- cognitoIdentityPoolId: Optional[str]
- principalOrgId: Optional[str]
+ accessKey: Optional[str] = None
+ accountId: Optional[str] = None
+ apiKey: Optional[str] = None
+ apiKeyId: Optional[str] = None
+ caller: Optional[str] = None
+ cognitoAuthenticationProvider: Optional[str] = None
+ cognitoAuthenticationType: Optional[str] = None
+ cognitoIdentityId: Optional[str] = None
+ cognitoIdentityPoolId: Optional[str] = None
+ principalOrgId: Optional[str] = None
# see #1562, temp workaround until API Gateway fixes it the Test button payload
# removing it will not be considered a regression in the future
sourceIp: Union[IPvAnyNetwork, Literal["test-invoke-source-ip"]]
- user: Optional[str]
- userAgent: Optional[str]
- userArn: Optional[str]
- clientCert: Optional[ApiGatewayUserCert]
+ user: Optional[str] = None
+ userAgent: Optional[str] = None
+ userArn: Optional[str] = None
+ clientCert: Optional[ApiGatewayUserCert] = None
class APIGatewayEventAuthorizer(BaseModel):
- claims: Optional[Dict[str, Any]]
- scopes: Optional[List[str]]
+ claims: Optional[Dict[str, Any]] = None
+ scopes: Optional[List[str]] = None
class APIGatewayEventRequestContext(BaseModel):
accountId: str
apiId: str
- authorizer: Optional[APIGatewayEventAuthorizer]
+ authorizer: Optional[APIGatewayEventAuthorizer] = None
stage: str
protocol: str
identity: APIGatewayEventIdentity
requestId: str
requestTime: str
requestTimeEpoch: datetime
- resourceId: Optional[str]
+ resourceId: Optional[str] = None
resourcePath: str
- domainName: Optional[str]
- domainPrefix: Optional[str]
- extendedRequestId: Optional[str]
+ domainName: Optional[str] = None
+ domainPrefix: Optional[str] = None
+ extendedRequestId: Optional[str] = None
httpMethod: Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
path: str
- connectedAt: Optional[datetime]
- connectionId: Optional[str]
- eventType: Optional[Literal["CONNECT", "MESSAGE", "DISCONNECT"]]
- messageDirection: Optional[str]
- messageId: Optional[str]
- routeKey: Optional[str]
- operationName: Optional[str]
+ connectedAt: Optional[datetime] = None
+ connectionId: Optional[str] = None
+ eventType: Optional[Literal["CONNECT", "MESSAGE", "DISCONNECT"]] = None
+ messageDirection: Optional[str] = None
+ messageId: Optional[str] = None
+ routeKey: Optional[str] = None
+ operationName: Optional[str] = None
- @root_validator(allow_reuse=True)
+ @root_validator(allow_reuse=True, skip_on_failure=True)
def check_message_id(cls, values):
message_id, event_type = values.get("messageId"), values.get("eventType")
if message_id is not None and event_type != "MESSAGE":
- raise TypeError("messageId is available only when the `eventType` is `MESSAGE`")
+ raise ValueError("messageId is available only when the `eventType` is `MESSAGE`")
return values
class APIGatewayProxyEventModel(BaseModel):
- version: Optional[str]
+ version: Optional[str] = None
resource: str
path: str
httpMethod: Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
headers: Dict[str, str]
multiValueHeaders: Dict[str, List[str]]
- queryStringParameters: Optional[Dict[str, str]]
- multiValueQueryStringParameters: Optional[Dict[str, List[str]]]
+ queryStringParameters: Optional[Dict[str, str]] = None
+ multiValueQueryStringParameters: Optional[Dict[str, List[str]]] = None
requestContext: APIGatewayEventRequestContext
- pathParameters: Optional[Dict[str, str]]
- stageVariables: Optional[Dict[str, str]]
+ pathParameters: Optional[Dict[str, str]] = None
+ stageVariables: Optional[Dict[str, str]] = None
isBase64Encoded: bool
- body: Optional[Union[str, Type[BaseModel]]]
+ body: Optional[Union[str, Type[BaseModel]]] = None
diff --git a/aws_lambda_powertools/utilities/parser/models/apigwv2.py b/aws_lambda_powertools/utilities/parser/models/apigwv2.py
index cb1f830bb47..3be793dd951 100644
--- a/aws_lambda_powertools/utilities/parser/models/apigwv2.py
+++ b/aws_lambda_powertools/utilities/parser/models/apigwv2.py
@@ -14,13 +14,13 @@ class RequestContextV2AuthorizerIamCognito(BaseModel):
class RequestContextV2AuthorizerIam(BaseModel):
- accessKey: Optional[str]
- accountId: Optional[str]
- callerId: Optional[str]
- principalOrgId: Optional[str]
- userArn: Optional[str]
- userId: Optional[str]
- cognitoIdentity: Optional[RequestContextV2AuthorizerIamCognito]
+ accessKey: Optional[str] = None
+ accountId: Optional[str] = None
+ callerId: Optional[str] = None
+ principalOrgId: Optional[str] = None
+ userArn: Optional[str] = None
+ userId: Optional[str] = None
+ cognitoIdentity: Optional[RequestContextV2AuthorizerIamCognito] = None
class RequestContextV2AuthorizerJwt(BaseModel):
@@ -29,8 +29,8 @@ class RequestContextV2AuthorizerJwt(BaseModel):
class RequestContextV2Authorizer(BaseModel):
- jwt: Optional[RequestContextV2AuthorizerJwt]
- iam: Optional[RequestContextV2AuthorizerIam]
+ jwt: Optional[RequestContextV2AuthorizerJwt] = None
+ iam: Optional[RequestContextV2AuthorizerIam] = None
lambda_value: Optional[Dict[str, Any]] = Field(None, alias="lambda")
@@ -45,7 +45,7 @@ class RequestContextV2Http(BaseModel):
class RequestContextV2(BaseModel):
accountId: str
apiId: str
- authorizer: Optional[RequestContextV2Authorizer]
+ authorizer: Optional[RequestContextV2Authorizer] = None
domainName: str
domainPrefix: str
requestId: str
@@ -61,11 +61,11 @@ class APIGatewayProxyEventV2Model(BaseModel):
routeKey: str
rawPath: str
rawQueryString: str
- cookies: Optional[List[str]]
+ cookies: Optional[List[str]] = None
headers: Dict[str, str]
- queryStringParameters: Optional[Dict[str, str]]
- pathParameters: Optional[Dict[str, str]]
- stageVariables: Optional[Dict[str, str]]
+ queryStringParameters: Optional[Dict[str, str]] = None
+ pathParameters: Optional[Dict[str, str]] = None
+ stageVariables: Optional[Dict[str, str]] = None
requestContext: RequestContextV2
- body: Optional[Union[str, Type[BaseModel]]]
+ body: Optional[Union[str, Type[BaseModel]]] = None
isBase64Encoded: bool
diff --git a/aws_lambda_powertools/utilities/parser/models/dynamodb.py b/aws_lambda_powertools/utilities/parser/models/dynamodb.py
index 7a12bf195d3..679952a7181 100644
--- a/aws_lambda_powertools/utilities/parser/models/dynamodb.py
+++ b/aws_lambda_powertools/utilities/parser/models/dynamodb.py
@@ -7,10 +7,10 @@
class DynamoDBStreamChangedRecordModel(BaseModel):
- ApproximateCreationDateTime: Optional[date]
+ ApproximateCreationDateTime: Optional[date] = None
Keys: Dict[str, Dict[str, Any]]
- NewImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]]
- OldImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]]
+ NewImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]] = None
+ OldImage: Optional[Union[Dict[str, Any], Type[BaseModel], BaseModel]] = None
SequenceNumber: str
SizeBytes: int
StreamViewType: Literal["NEW_AND_OLD_IMAGES", "KEYS_ONLY", "NEW_IMAGE", "OLD_IMAGE"]
@@ -40,7 +40,7 @@ class DynamoDBStreamRecordModel(BaseModel):
awsRegion: str
eventSourceARN: str
dynamodb: DynamoDBStreamChangedRecordModel
- userIdentity: Optional[UserIdentity]
+ userIdentity: Optional[UserIdentity] = None
class DynamoDBStreamModel(BaseModel):
diff --git a/aws_lambda_powertools/utilities/parser/models/kafka.py b/aws_lambda_powertools/utilities/parser/models/kafka.py
index d4c36bf70f1..1d9d8114e65 100644
--- a/aws_lambda_powertools/utilities/parser/models/kafka.py
+++ b/aws_lambda_powertools/utilities/parser/models/kafka.py
@@ -19,8 +19,8 @@ class KafkaRecordModel(BaseModel):
value: Union[str, Type[BaseModel]]
headers: List[Dict[str, bytes]]
- # validators
- _decode_key = validator("key", allow_reuse=True)(base64_decode)
+ # Added type ignore to keep compatibility between Pydantic v1 and v2
+ _decode_key = validator("key", allow_reuse=True)(base64_decode) # type: ignore[type-var, unused-ignore]
@validator("value", pre=True, allow_reuse=True)
def data_base64_decode(cls, value):
diff --git a/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py b/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py
index c59d8c680e5..7edc0ba4ebf 100644
--- a/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py
+++ b/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py
@@ -17,7 +17,7 @@ class KinesisFirehoseRecord(BaseModel):
data: Union[bytes, Type[BaseModel]] # base64 encoded str is parsed into bytes
recordId: str
approximateArrivalTimestamp: PositiveInt
- kinesisRecordMetadata: Optional[KinesisFirehoseRecordMetadata]
+ kinesisRecordMetadata: Optional[KinesisFirehoseRecordMetadata] = None
@validator("data", pre=True, allow_reuse=True)
def data_base64_decode(cls, value):
@@ -28,5 +28,5 @@ class KinesisFirehoseModel(BaseModel):
invocationId: str
deliveryStreamArn: str
region: str
- sourceKinesisStreamArn: Optional[str]
+ sourceKinesisStreamArn: Optional[str] = None
records: List[KinesisFirehoseRecord]
diff --git a/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py b/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py
index b649828853b..58a23e5006c 100644
--- a/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py
+++ b/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py
@@ -13,7 +13,7 @@ class KinesisFirehoseSqsRecord(BaseModel):
data: SqsRecordModel
recordId: str
approximateArrivalTimestamp: PositiveInt
- kinesisRecordMetadata: Optional[KinesisFirehoseRecordMetadata]
+ kinesisRecordMetadata: Optional[KinesisFirehoseRecordMetadata] = None
@validator("data", pre=True, allow_reuse=True)
def data_base64_decode(cls, value):
@@ -25,5 +25,5 @@ class KinesisFirehoseSqsModel(BaseModel):
invocationId: str
deliveryStreamArn: str
region: str
- sourceKinesisStreamArn: Optional[str]
+ sourceKinesisStreamArn: Optional[str] = None
records: List[KinesisFirehoseSqsRecord]
diff --git a/aws_lambda_powertools/utilities/parser/models/s3.py b/aws_lambda_powertools/utilities/parser/models/s3.py
index 01573b6d751..db6c41d30f3 100644
--- a/aws_lambda_powertools/utilities/parser/models/s3.py
+++ b/aws_lambda_powertools/utilities/parser/models/s3.py
@@ -45,10 +45,10 @@ class S3Bucket(BaseModel):
class S3Object(BaseModel):
key: str
- size: Optional[NonNegativeFloat]
- eTag: Optional[str]
+ size: Optional[NonNegativeFloat] = None
+ eTag: Optional[str] = None
sequencer: str
- versionId: Optional[str]
+ versionId: Optional[str] = None
class S3Message(BaseModel):
@@ -60,10 +60,10 @@ class S3Message(BaseModel):
class S3EventNotificationObjectModel(BaseModel):
key: str
- size: Optional[NonNegativeFloat]
+ size: Optional[NonNegativeFloat] = None
etag: str
version_id: str = Field(None, alias="version-id")
- sequencer: Optional[str]
+ sequencer: Optional[str] = None
class S3EventNotificationEventBridgeBucketModel(BaseModel):
@@ -77,7 +77,7 @@ class S3EventNotificationEventBridgeDetailModel(BaseModel):
request_id: str = Field(None, alias="request-id")
requester: str
source_ip_address: str = Field(None, alias="source-ip-address")
- reason: Optional[str]
+ reason: Optional[str] = None
deletion_type: Optional[str] = Field(None, alias="deletion-type")
restore_expiry_time: Optional[str] = Field(None, alias="restore-expiry-time")
source_storage_class: Optional[str] = Field(None, alias="source-storage-class")
@@ -99,9 +99,9 @@ class S3RecordModel(BaseModel):
requestParameters: S3RequestParameters
responseElements: S3ResponseElements
s3: S3Message
- glacierEventData: Optional[S3EventRecordGlacierEventData]
+ glacierEventData: Optional[S3EventRecordGlacierEventData] = None
- @root_validator
+ @root_validator(allow_reuse=True, skip_on_failure=True)
def validate_s3_object(cls, values):
event_name = values.get("eventName")
s3_object = values.get("s3").object
diff --git a/aws_lambda_powertools/utilities/parser/models/s3_object_event.py b/aws_lambda_powertools/utilities/parser/models/s3_object_event.py
index ef59e9c2f98..7ef98fe4bb2 100644
--- a/aws_lambda_powertools/utilities/parser/models/s3_object_event.py
+++ b/aws_lambda_powertools/utilities/parser/models/s3_object_event.py
@@ -22,7 +22,7 @@ class S3ObjectUserRequest(BaseModel):
class S3ObjectSessionIssuer(BaseModel):
type: str # noqa: A003, VNE003
- userName: Optional[str]
+ userName: Optional[str] = None
principalId: str
arn: str
accountId: str
@@ -42,10 +42,10 @@ class S3ObjectUserIdentity(BaseModel):
type: str # noqa003
accountId: str
accessKeyId: str
- userName: Optional[str]
+ userName: Optional[str] = None
principalId: str
arn: str
- sessionContext: Optional[S3ObjectSessionContext]
+ sessionContext: Optional[S3ObjectSessionContext] = None
class S3ObjectLambdaEvent(BaseModel):
diff --git a/aws_lambda_powertools/utilities/parser/models/ses.py b/aws_lambda_powertools/utilities/parser/models/ses.py
index 77b23431099..2e9e93f368e 100644
--- a/aws_lambda_powertools/utilities/parser/models/ses.py
+++ b/aws_lambda_powertools/utilities/parser/models/ses.py
@@ -36,9 +36,9 @@ class SesMailHeaders(BaseModel):
class SesMailCommonHeaders(BaseModel):
header_from: List[str] = Field(None, alias="from")
to: List[str]
- cc: Optional[List[str]]
- bcc: Optional[List[str]]
- sender: Optional[List[str]]
+ cc: Optional[List[str]] = None
+ bcc: Optional[List[str]] = None
+ sender: Optional[List[str]] = None
reply_to: Optional[List[str]] = Field(None, alias="reply-to")
returnPath: str
messageId: str
diff --git a/aws_lambda_powertools/utilities/parser/models/sns.py b/aws_lambda_powertools/utilities/parser/models/sns.py
index 6cd2fcec006..8f388f2974c 100644
--- a/aws_lambda_powertools/utilities/parser/models/sns.py
+++ b/aws_lambda_powertools/utilities/parser/models/sns.py
@@ -14,17 +14,17 @@ class SnsMsgAttributeModel(BaseModel):
class SnsNotificationModel(BaseModel):
- Subject: Optional[str]
+ Subject: Optional[str] = None
TopicArn: str
UnsubscribeUrl: HttpUrl
Type: Literal["Notification"]
- MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]]
+ MessageAttributes: Optional[Dict[str, SnsMsgAttributeModel]] = None
Message: Union[str, TypingType[BaseModel]]
MessageId: str
- SigningCertUrl: Optional[HttpUrl] # NOTE: FIFO opt-in removes attribute
- Signature: Optional[str] # NOTE: FIFO opt-in removes attribute
+ SigningCertUrl: Optional[HttpUrl] = None # NOTE: FIFO opt-in removes attribute
+ Signature: Optional[str] = None # NOTE: FIFO opt-in removes attribute
Timestamp: datetime
- SignatureVersion: Optional[str] # NOTE: FIFO opt-in removes attribute
+ SignatureVersion: Optional[str] = None # NOTE: FIFO opt-in removes attribute
@root_validator(pre=True, allow_reuse=True)
def check_sqs_protocol(cls, values):
diff --git a/aws_lambda_powertools/utilities/parser/models/sqs.py b/aws_lambda_powertools/utilities/parser/models/sqs.py
index 168707530f3..63ea4b76e0e 100644
--- a/aws_lambda_powertools/utilities/parser/models/sqs.py
+++ b/aws_lambda_powertools/utilities/parser/models/sqs.py
@@ -9,17 +9,17 @@
class SqsAttributesModel(BaseModel):
ApproximateReceiveCount: str
ApproximateFirstReceiveTimestamp: datetime
- MessageDeduplicationId: Optional[str]
- MessageGroupId: Optional[str]
+ MessageDeduplicationId: Optional[str] = None
+ MessageGroupId: Optional[str] = None
SenderId: str
SentTimestamp: datetime
- SequenceNumber: Optional[str]
- AWSTraceHeader: Optional[str]
+ SequenceNumber: Optional[str] = None
+ AWSTraceHeader: Optional[str] = None
class SqsMsgAttributeModel(BaseModel):
- stringValue: Optional[str]
- binaryValue: Optional[str]
+ stringValue: Optional[str] = None
+ binaryValue: Optional[str] = None
stringListValues: List[str] = []
binaryListValues: List[str] = []
dataType: str
@@ -56,7 +56,7 @@ class SqsRecordModel(BaseModel):
attributes: SqsAttributesModel
messageAttributes: Dict[str, SqsMsgAttributeModel]
md5OfBody: str
- md5OfMessageAttributes: Optional[str]
+ md5OfMessageAttributes: Optional[str] = None
eventSource: Literal["aws:sqs"]
eventSourceARN: str
awsRegion: str
diff --git a/aws_lambda_powertools/utilities/parser/parser.py b/aws_lambda_powertools/utilities/parser/parser.py
index eeaa5612fff..7e2d69e429c 100644
--- a/aws_lambda_powertools/utilities/parser/parser.py
+++ b/aws_lambda_powertools/utilities/parser/parser.py
@@ -1,6 +1,7 @@
import logging
from typing import Any, Callable, Dict, Optional, Type, overload
+from aws_lambda_powertools.utilities.parser.compat import disable_pydantic_v2_warning
from aws_lambda_powertools.utilities.parser.types import EventParserReturnType, Model
from ...middleware_factory import lambda_handler_decorator
@@ -156,6 +157,7 @@ def handler(event: Order, context: LambdaContext):
raise InvalidEnvelopeError(f"Envelope must implement BaseEnvelope, envelope={envelope}")
try:
+ disable_pydantic_v2_warning()
logger.debug("Parsing and validating event model; no envelope used")
if isinstance(event, str):
return model.parse_raw(event)
diff --git a/docs/Dockerfile b/docs/Dockerfile
index 50b8f6682c2..4d24b69c3f0 100644
--- a/docs/Dockerfile
+++ b/docs/Dockerfile
@@ -1,5 +1,5 @@
# v9.1.18
-FROM squidfunk/mkdocs-material@sha256:a28ed811514d6c6cf15a4bcf7a2ce21d03fcb5c8cbdc4e1fec8ba179ef58fbb5
+FROM squidfunk/mkdocs-material@sha256:33e28bdae302bc1aa9c6783dd863742416cb1174bae4ad9d7bcc5b2efe685639
# pip-compile --generate-hashes --output-file=requirements.txt requirements.in
COPY requirements.txt /tmp/
RUN pip install --require-hashes -r /tmp/requirements.txt
diff --git a/docs/index.md b/docs/index.md
index aab8f441b1e..215450e8421 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -26,8 +26,8 @@ Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverles
You can install Powertools for AWS Lambda (Python) using one of the following options:
-* **Lambda Layer (x86_64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37**](# "Replace {region} with your AWS region, e.g., eu-west-1"){: .copyMe}:clipboard:
-* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37**](# "Replace {region} with your AWS region, e.g., eu-west-1"){: .copyMe}:clipboard:
+* **Lambda Layer (x86_64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38**](# "Replace {region} with your AWS region, e.g., eu-west-1"){: .copyMe}:clipboard:
+* **Lambda Layer (arm64)**: [**arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38**](# "Replace {region} with your AWS region, e.g., eu-west-1"){: .copyMe}:clipboard:
* **Pip**: **[`pip install "aws-lambda-powertools"`](#){: .copyMe}:clipboard:**
!!! question "Looking for Pip signed releases? [Learn more about verifying signed builds](./security.md#verifying-signed-builds)"
@@ -80,60 +80,60 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
| Region | Layer ARN |
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
- | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-south-2` | [arn:aws:lambda:ap-south-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-4` | [arn:aws:lambda:ap-southeast-4:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-central-2` | [arn:aws:lambda:eu-central-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-south-2` | [arn:aws:lambda:eu-south-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `me-central-1` | [arn:aws:lambda:me-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:37](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-south-2` | [arn:aws:lambda:ap-south-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-4` | [arn:aws:lambda:ap-southeast-4:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-central-2` | [arn:aws:lambda:eu-central-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-south-2` | [arn:aws:lambda:eu-south-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `me-central-1` | [arn:aws:lambda:me-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2:38](#){: .copyMe}:clipboard: |
=== "arm64"
| Region | Layer ARN |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
- | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
- | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37](#){: .copyMe}:clipboard: |
+ | `af-south-1` | [arn:aws:lambda:af-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-east-1` | [arn:aws:lambda:ap-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-south-1` | [arn:aws:lambda:ap-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `ca-central-1` | [arn:aws:lambda:ca-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-central-1` | [arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-north-1` | [arn:aws:lambda:eu-north-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-south-1` | [arn:aws:lambda:eu-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-west-1` | [arn:aws:lambda:eu-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-west-2` | [arn:aws:lambda:eu-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `eu-west-3` | [arn:aws:lambda:eu-west-3:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `me-south-1` | [arn:aws:lambda:me-south-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `sa-east-1` | [arn:aws:lambda:sa-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `us-east-1` | [arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `us-east-2` | [arn:aws:lambda:us-east-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `us-west-1` | [arn:aws:lambda:us-west-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
+ | `us-west-2` | [arn:aws:lambda:us-west-2:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38](#){: .copyMe}:clipboard: |
??? note "Note: Click to expand and copy code snippets for popular frameworks"
@@ -146,7 +146,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
Type: AWS::Serverless::Function
Properties:
Layers:
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
```
=== "Serverless framework"
@@ -156,7 +156,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
hello:
handler: lambda_function.lambda_handler
layers:
- - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
```
=== "CDK"
@@ -172,7 +172,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
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:37"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38"
)
aws_lambda.Function(self,
'sample-app-lambda',
@@ -221,7 +221,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "python3.9"
- layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38"]
source_code_hash = filebase64sha256("lambda_function_payload.zip")
}
@@ -274,7 +274,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
? 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:37
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
❯ amplify push -y
@@ -285,7 +285,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
- 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:37
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
? Do you want to edit the local lambda function now? No
```
@@ -299,7 +299,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
Properties:
Architectures: [arm64]
Layers:
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38
```
=== "Serverless framework"
@@ -310,7 +310,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
handler: lambda_function.lambda_handler
architecture: arm64
layers:
- - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37
+ - arn:aws:lambda:${aws:region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38
```
=== "CDK"
@@ -326,7 +326,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
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:37"
+ layer_version_arn=f"arn:aws:lambda:{env.region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38"
)
aws_lambda.Function(self,
'sample-app-lambda',
@@ -376,7 +376,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
role = aws_iam_role.iam_for_lambda.arn
handler = "index.test"
runtime = "python3.9"
- layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:37"]
+ layers = ["arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38"]
architectures = ["arm64"]
source_code_hash = filebase64sha256("lambda_function_payload.zip")
@@ -432,7 +432,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
? 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:37
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38
❯ amplify push -y
@@ -443,7 +443,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
- 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:37
+ ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:eu-central-1:017000801446:layer:AWSLambdaPowertoolsPythonV2-Arm64:38
? Do you want to edit the local lambda function now? No
```
@@ -451,7 +451,7 @@ You can include Powertools for AWS Lambda (Python) Lambda Layer using [AWS Lambd
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:37 --region {region}
+ aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38 --region {region}
```
The pre-signed URL to download this Lambda Layer will be within `Location` key.
@@ -753,6 +753,7 @@ The following companies, among others, use Powertools:
* [CyberArk](https://www.cyberark.com/){target="_blank"}
* [globaldatanet](https://globaldatanet.com/){target="_blank"}
* [IMS](https://ims.tech/){target="_blank"}
+* [Jit Security](https://www.jit.io/){target="_blank"}
* [Propellor.ai](https://www.propellor.ai/){target="_blank"}
* [TopSport](https://www.topsport.com.au/){target="_blank"}
* [Trek10](https://www.trek10.com/){target="_blank"}
diff --git a/docs/tutorial/index.md b/docs/tutorial/index.md
index 685cad753f3..c951a6205df 100644
--- a/docs/tutorial/index.md
+++ b/docs/tutorial/index.md
@@ -649,7 +649,7 @@ Within AWS X-Ray, we can answer these questions by using two features: tracing *
Let's put them into action.
-```python title="Enriching traces with annotations and metadata" hl_lines="10 17-18 26-27 35 37-42 45"
+```python title="Enriching traces with annotations and metadata" hl_lines="10 17-18 26-27 35 37-41 44"
from aws_xray_sdk.core import patch_all, xray_recorder
from aws_lambda_powertools import Logger
@@ -687,11 +687,10 @@ def lambda_handler(event, context):
global cold_start
subsegment = xray_recorder.current_subsegment()
+ subsegment.put_annotation(key="ColdStart", value=cold_start)
+
if cold_start:
- subsegment.put_annotation(key="ColdStart", value=cold_start)
cold_start = False
- else:
- subsegment.put_annotation(key="ColdStart", value=cold_start)
result = app.resolve(event, context)
subsegment.put_metadata("response", result)
@@ -705,8 +704,8 @@ Let's break it down:
* **L17-18**: We use AWS X-Ray SDK to add `User` annotation on `hello_name` subsegment. This will allow us to filter traces using the `User` value.
* **L26-27**: We repeat what we did in L17-18 except we use the value `unknown` since we don't have that information.
* **L35**: We use `global` to modify our global variable defined in the outer scope.
-* **37-42**: We add `ColdStart` annotation and flip the value of `cold_start` variable, so that subsequent requests annotates the value `false` when the sandbox is reused.
-* **L45**: We include the final response under `response` key as part of the `handler` subsegment.
+* **37-41**: We add `ColdStart` annotation and set `cold_start` variable to `false`, so that subsequent requests annotates the value `false` when the sandbox is reused.
+* **L44**: We include the final response under `response` key as part of the `handler` subsegment.
???+ info
If you want to understand how the Lambda execution environment (sandbox) works and why cold starts can occur, see this [blog series on Lambda performance](https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/){target="_blank"}.
diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md
index f482dcb0410..d98835a8381 100644
--- a/docs/utilities/parser.md
+++ b/docs/utilities/parser.md
@@ -11,14 +11,19 @@ This utility provides data parsing and deep validation using [Pydantic](https://
* Defines data in pure Python classes, then parse, validate and extract only what you want
* Built-in envelopes to unwrap, extend, and validate popular event sources payloads
* Enforces type hints at runtime with user-friendly errors
+* Support for Pydantic v1 and v2
## Getting started
### Install
+Powertools for AWS Lambda (Python) supports Pydantic v1 and v2. Each Pydantic version requires different dependencies before you can use Parser.
+
+#### Using Pydantic v1
+
!!! info "This is not necessary if you're installing Powertools for AWS Lambda (Python) via [Lambda Layer/SAR](../index.md#lambda-layer){target="_blank"}"
-Add `aws-lambda-powertools[parser]` as a dependency in your preferred tool: _e.g._, _requirements.txt_, _pyproject.toml_. This will ensure you have the required dependencies before using Parser.
+Add `aws-lambda-powertools[parser]` as a dependency in your preferred tool: _e.g._, _requirements.txt_, _pyproject.toml_.
???+ warning
This will increase the compressed package size by >10MB due to the Pydantic dependency.
@@ -28,6 +33,12 @@ Add `aws-lambda-powertools[parser]` as a dependency in your preferred tool: _e.g
Pip example: `SKIP_CYTHON=1 pip install --no-binary pydantic aws-lambda-powertools[parser]`
+#### Using Pydantic v2
+
+You need to bring Pydantic v2.0.3 or later as an external dependency. Note that [we suppress Pydantic v2 deprecation warnings](https://github.com/aws-powertools/powertools-lambda-python/issues/2672){target="_blank"} to reduce noise and optimize log costs.
+
+Add `aws-lambda-powertools` and `pydantic>=2.0.3` as a dependency in your preferred tool: _e.g._, _requirements.txt_, _pyproject.toml_.
+
### Defining models
You can define models to parse incoming events by inheriting from `BaseModel`.
@@ -45,7 +56,7 @@ class Order(BaseModel):
id: int
description: str
items: List[OrderItem] # nesting models are supported
- optional_field: Optional[str] # this field may or may not be available when parsing
+ optional_field: Optional[str] = None # this field may or may not be available when parsing
```
These are simply Python classes that inherit from BaseModel. **Parser** enforces type hints declared in your model at runtime.
@@ -79,7 +90,7 @@ class Order(BaseModel):
id: int
description: str
items: List[OrderItem] # nesting models are supported
- optional_field: Optional[str] # this field may or may not be available when parsing
+ optional_field: Optional[str] = None # this field may or may not be available when parsing
@event_parser(model=Order)
@@ -124,7 +135,7 @@ class Order(BaseModel):
id: int
description: str
items: List[OrderItem] # nesting models are supported
- optional_field: Optional[str] # this field may or may not be available when parsing
+ optional_field: Optional[str] = None # this field may or may not be available when parsing
payload = {
diff --git a/examples/logger/sam/template.yaml b/examples/logger/sam/template.yaml
index 2472b332651..a9842f53348 100644
--- a/examples/logger/sam/template.yaml
+++ b/examples/logger/sam/template.yaml
@@ -14,7 +14,7 @@ Globals:
Layers:
# Find the latest Layer version in the official documentation
# https://docs.powertools.aws.dev/lambda/python/latest/#lambda-layer
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
Resources:
LoggerLambdaHandlerExample:
diff --git a/examples/metrics/sam/template.yaml b/examples/metrics/sam/template.yaml
index c49b9e0f733..c48ac3dbaa1 100644
--- a/examples/metrics/sam/template.yaml
+++ b/examples/metrics/sam/template.yaml
@@ -15,7 +15,7 @@ Globals:
Layers:
# Find the latest Layer version in the official documentation
# https://docs.powertools.aws.dev/lambda/python/latest/#lambda-layer
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
Resources:
CaptureLambdaHandlerExample:
diff --git a/examples/tracer/sam/template.yaml b/examples/tracer/sam/template.yaml
index 6cc811d1b2a..1d22b83865b 100644
--- a/examples/tracer/sam/template.yaml
+++ b/examples/tracer/sam/template.yaml
@@ -13,7 +13,7 @@ Globals:
Layers:
# Find the latest Layer version in the official documentation
# https://docs.powertools.aws.dev/lambda/python/latest/#lambda-layer
- - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:37
+ - !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:38
Resources:
CaptureLambdaHandlerExample:
diff --git a/package-lock.json b/package-lock.json
index 8921e96a70a..64ddf8ac968 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,13 +11,13 @@
"package-lock.json": "^1.0.0"
},
"devDependencies": {
- "aws-cdk": "^2.87.0"
+ "aws-cdk": "^2.88.0"
}
},
"node_modules/aws-cdk": {
- "version": "2.87.0",
- "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.87.0.tgz",
- "integrity": "sha512-dBm74nl3dMUxoAzgjcfKnzJyoVNIV//B1sqDN11cC3LXEflYapcBxPxZHAyGcRXg5dW3m14dMdKVQfmt4N970g==",
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.88.0.tgz",
+ "integrity": "sha512-7Tj0uusA2nsEOsqkd4kB5vmzciz7l/eGBN5a+Ce4/CCcoe4ZCvT85L+T6tK0aohUTLZTAlTPBceH34RN5iMYpA==",
"dev": true,
"bin": {
"cdk": "bin/cdk"
@@ -51,9 +51,9 @@
},
"dependencies": {
"aws-cdk": {
- "version": "2.87.0",
- "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.87.0.tgz",
- "integrity": "sha512-dBm74nl3dMUxoAzgjcfKnzJyoVNIV//B1sqDN11cC3LXEflYapcBxPxZHAyGcRXg5dW3m14dMdKVQfmt4N970g==",
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.88.0.tgz",
+ "integrity": "sha512-7Tj0uusA2nsEOsqkd4kB5vmzciz7l/eGBN5a+Ce4/CCcoe4ZCvT85L+T6tK0aohUTLZTAlTPBceH34RN5iMYpA==",
"dev": true,
"requires": {
"fsevents": "2.3.2"
diff --git a/package.json b/package.json
index 643bec22883..396ea6c7921 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "aws-lambda-powertools-python-e2e",
"version": "1.0.0",
"devDependencies": {
- "aws-cdk": "^2.87.0"
+ "aws-cdk": "^2.88.0"
},
"dependencies": {
"package-lock.json": "^1.0.0"
diff --git a/poetry.lock b/poetry.lock
index d38f6010bb4..db21a2db772 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1331,13 +1331,13 @@ mkdocs = ">=0.17"
[[package]]
name = "mkdocs-material"
-version = "9.1.18"
+version = "9.1.19"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mkdocs_material-9.1.18-py3-none-any.whl", hash = "sha256:5bcf8fb79ac2f253c0ffe93fa181cba87718c6438f459dc4180ac7418cc9a450"},
- {file = "mkdocs_material-9.1.18.tar.gz", hash = "sha256:981dd39979723d4cda7cfc77bbbe5e54922d5761a7af23fb8ba9edb52f114b13"},
+ {file = "mkdocs_material-9.1.19-py3-none-any.whl", hash = "sha256:fb0a149294b319aedf36983919d8c40c9e566db21ead16258e20ebd2e6c0961c"},
+ {file = "mkdocs_material-9.1.19.tar.gz", hash = "sha256:73b94b08c765e92a80645aac58d6a741fc5f587deec2b715489c714827b15a6f"},
]
[package.dependencies]
@@ -1526,13 +1526,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.9\""}
[[package]]
name = "mypy-boto3-s3"
-version = "1.28.3"
-description = "Type annotations for boto3.S3 1.28.3 service generated with mypy-boto3-builder 7.14.6"
+version = "1.28.8"
+description = "Type annotations for boto3.S3 1.28.8 service generated with mypy-boto3-builder 7.15.1"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mypy-boto3-s3-1.28.3.tar.gz", hash = "sha256:c2b454413126079a6434a3e05a75cb4cad947c7624e53ba3846cb829a2230c4b"},
- {file = "mypy_boto3_s3-1.28.3-py3-none-any.whl", hash = "sha256:58d4e65c9dabbdc9a8896f7cd3d6475f4331183220a2b41ba1862bf5b3c592bc"},
+ {file = "mypy-boto3-s3-1.28.8.tar.gz", hash = "sha256:c9ed17fee2c0e2edeb2966b3796af7b349dcc4eeee54dbd59a269fdb9418eb55"},
+ {file = "mypy_boto3_s3-1.28.8-py3-none-any.whl", hash = "sha256:75b929c517c5ad8f97c14dfba5f8521db569157dc4ac76a07a178805777cff8c"},
]
[package.dependencies]
@@ -1540,13 +1540,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.9\""}
[[package]]
name = "mypy-boto3-secretsmanager"
-version = "1.28.3"
-description = "Type annotations for boto3.SecretsManager 1.28.3 service generated with mypy-boto3-builder 7.14.6"
+version = "1.28.3.post2"
+description = "Type annotations for boto3.SecretsManager 1.28.3 service generated with mypy-boto3-builder 7.15.0"
optional = false
python-versions = ">=3.7"
files = [
- {file = "mypy-boto3-secretsmanager-1.28.3.tar.gz", hash = "sha256:d8e07c8d33392a626b174b6681df3b4c9dfe6ea76de00bda891720d26e63889a"},
- {file = "mypy_boto3_secretsmanager-1.28.3-py3-none-any.whl", hash = "sha256:c662b6ea74a218c801f692012d1d86e0f7f9f9fc6d3a53e7d6e99fae7d87e2c1"},
+ {file = "mypy-boto3-secretsmanager-1.28.3.post2.tar.gz", hash = "sha256:f359f6446ac856d0887e40cb0f5bc6e0a60873524be5dd4b68be1d0fc4ac513e"},
+ {file = "mypy_boto3_secretsmanager-1.28.3.post2-py3-none-any.whl", hash = "sha256:3a5e5619ee945f244d2dfefcb382c85874171a18b46f75403465622095284d25"},
]
[package.dependencies]
@@ -2510,24 +2510,24 @@ test = ["mypy", "pytest", "typing-extensions"]
[[package]]
name = "types-python-dateutil"
-version = "2.8.19.13"
+version = "2.8.19.14"
description = "Typing stubs for python-dateutil"
optional = false
python-versions = "*"
files = [
- {file = "types-python-dateutil-2.8.19.13.tar.gz", hash = "sha256:09a0275f95ee31ce68196710ed2c3d1b9dc42e0b61cc43acc369a42cb939134f"},
- {file = "types_python_dateutil-2.8.19.13-py3-none-any.whl", hash = "sha256:0b0e7c68e7043b0354b26a1e0225cb1baea7abb1b324d02b50e2d08f1221043f"},
+ {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"},
+ {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"},
]
[[package]]
name = "types-requests"
-version = "2.31.0.1"
+version = "2.31.0.2"
description = "Typing stubs for requests"
optional = false
python-versions = "*"
files = [
- {file = "types-requests-2.31.0.1.tar.gz", hash = "sha256:3de667cffa123ce698591de0ad7db034a5317457a596eb0b4944e5a9d9e8d1ac"},
- {file = "types_requests-2.31.0.1-py3-none-any.whl", hash = "sha256:afb06ef8f25ba83d59a1d424bd7a5a939082f94b94e90ab5e6116bd2559deaa3"},
+ {file = "types-requests-2.31.0.2.tar.gz", hash = "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"},
+ {file = "types_requests-2.31.0.2-py3-none-any.whl", hash = "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a"},
]
[package.dependencies]
@@ -2749,4 +2749,4 @@ validation = ["fastjsonschema"]
[metadata]
lock-version = "2.0"
python-versions = "^3.7.4"
-content-hash = "a5c056fc77c478f4afcacebd74b687f8c8417fe53956fefc0526c407ebfd5424"
+content-hash = "b3614829c4901603d139ee5cdb4b17d3a6c66877ced79779b92d26573f4dd84f"
diff --git a/pyproject.toml b/pyproject.toml
index b82bf9ddba0..c83d9b0a04a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aws_lambda_powertools"
-version = "2.19.0"
+version = "2.21.0"
description = "Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless best practices and increase developer velocity."
authors = ["Amazon Web Services"]
include = ["aws_lambda_powertools/py.typed", "THIRD-PARTY-LICENSES"]
@@ -64,11 +64,11 @@ mypy-boto3-lambda = "^1.28.0"
mypy-boto3-logs = "^1.28.1"
mypy-boto3-secretsmanager = "^1.28.3"
mypy-boto3-ssm = "^1.28.0"
-mypy-boto3-s3 = "^1.28.3"
+mypy-boto3-s3 = "^1.28.8"
mypy-boto3-xray = "^1.28.0"
types-requests = "^2.31.0"
typing-extensions = "^4.6.2"
-mkdocs-material = "^9.1.17"
+mkdocs-material = "^9.1.19"
filelock = "^3.12.2"
checksumdir = "^1.2.0"
mypy-boto3-appconfigdata = "^1.28.0"
@@ -161,6 +161,15 @@ markers = [
"perf: marks perf tests to be deselected (deselect with '-m \"not perf\"')",
]
+# MAINTENANCE: Remove these lines when drop support to Pydantic v1
+filterwarnings=[
+ "ignore:.*The `parse_obj` method is deprecated*:DeprecationWarning",
+ "ignore:.*The `parse_raw` method is deprecated*:DeprecationWarning",
+ "ignore:.*load_str_bytes is deprecated*:DeprecationWarning",
+ "ignore:.*The `dict` method is deprecated; use `model_dump` instead*:DeprecationWarning",
+ "ignore:.*Pydantic V1 style `@validator` validators are deprecated*:DeprecationWarning"
+]
+
[build-system]
requires = ["poetry-core>=1.3.2"]
build-backend = "poetry.core.masonry.api"
diff --git a/ruff.toml b/ruff.toml
index f3a50abc720..424040ede1f 100644
--- a/ruff.toml
+++ b/ruff.toml
@@ -68,3 +68,4 @@ split-on-trailing-comma = true
"tests/e2e/utils/data_builder/__init__.py" = ["F401"]
"tests/e2e/utils/data_fetcher/__init__.py" = ["F401"]
"aws_lambda_powertools/utilities/data_classes/s3_event.py" = ["A003"]
+"aws_lambda_powertools/utilities/parser/models/__init__.py" = ["E402"]
diff --git a/tests/functional/batch/sample_models.py b/tests/functional/batch/sample_models.py
index 556ff0ebf8a..72029e154d5 100644
--- a/tests/functional/batch/sample_models.py
+++ b/tests/functional/batch/sample_models.py
@@ -35,12 +35,15 @@ class OrderDynamoDB(BaseModel):
# so Pydantic can auto-initialize nested Order model
@validator("Message", pre=True)
def transform_message_to_dict(cls, value: Dict[Literal["S"], str]):
- return json.loads(value["S"])
+ try:
+ return json.loads(value["S"])
+ except TypeError:
+ raise ValueError
class OrderDynamoDBChangeRecord(DynamoDBStreamChangedRecordModel):
- NewImage: Optional[OrderDynamoDB]
- OldImage: Optional[OrderDynamoDB]
+ NewImage: Optional[OrderDynamoDB] = None
+ OldImage: Optional[OrderDynamoDB] = None
class OrderDynamoDBRecord(DynamoDBStreamRecordModel):
diff --git a/tests/functional/parser/conftest.py b/tests/functional/parser/conftest.py
index 34199a322b2..41347bc5fa9 100644
--- a/tests/functional/parser/conftest.py
+++ b/tests/functional/parser/conftest.py
@@ -6,6 +6,15 @@
from aws_lambda_powertools.utilities.parser import BaseEnvelope
+@pytest.fixture
+def pydanticv2_only():
+ from pydantic import __version__
+
+ version = __version__.split(".")
+ if version[0] != "2":
+ pytest.skip("pydanticv2 test only")
+
+
@pytest.fixture
def dummy_event():
return {"payload": {"message": "hello world"}}
diff --git a/tests/functional/parser/test_parser.py b/tests/functional/parser/test_parser.py
index c439134071c..1f948655917 100644
--- a/tests/functional/parser/test_parser.py
+++ b/tests/functional/parser/test_parser.py
@@ -1,6 +1,7 @@
import json
from typing import Dict, Union
+import pydantic
import pytest
from aws_lambda_powertools.utilities.parser import (
@@ -53,6 +54,27 @@ def handle_no_envelope(event: Dict, _: LambdaContext):
handle_no_envelope(dummy_event["payload"], LambdaContext())
+@pytest.mark.usefixtures("pydanticv2_only")
+def test_pydanticv2_validation():
+ class FakeModel(pydantic.BaseModel):
+ region: str
+ event_name: str
+ version: int
+
+ # WHEN using the validator for v2
+ @pydantic.field_validator("version", mode="before")
+ def validate_field(cls, value):
+ return int(value)
+
+ event_raw = {"region": "us-east-1", "event_name": "aws-powertools", "version": "10"}
+ event_parsed = FakeModel(**event_raw)
+
+ # THEN parse the event as expected
+ assert event_parsed.region == event_raw["region"]
+ assert event_parsed.event_name == event_raw["event_name"]
+ assert event_parsed.version == int(event_raw["version"])
+
+
@pytest.mark.parametrize("invalid_schema", [None, str, bool(), [], (), object])
def test_parser_with_invalid_schema_type(dummy_event, invalid_schema):
@event_parser(model=invalid_schema)
diff --git a/tests/functional/test_utilities_batch.py b/tests/functional/test_utilities_batch.py
index 1831ef973d9..e146d65744f 100644
--- a/tests/functional/test_utilities_batch.py
+++ b/tests/functional/test_utilities_batch.py
@@ -501,8 +501,8 @@ def transform_message_to_dict(cls, value: Dict[Literal["S"], str]):
return json.loads(value["S"])
class OrderDynamoDBChangeRecord(DynamoDBStreamChangedRecordModel):
- NewImage: Optional[OrderDynamoDB]
- OldImage: Optional[OrderDynamoDB]
+ NewImage: Optional[OrderDynamoDB] = None
+ OldImage: Optional[OrderDynamoDB] = None
class OrderDynamoDBRecord(DynamoDBStreamRecordModel):
dynamodb: OrderDynamoDBChangeRecord
@@ -545,8 +545,8 @@ def transform_message_to_dict(cls, value: Dict[Literal["S"], str]):
return json.loads(value["S"])
class OrderDynamoDBChangeRecord(DynamoDBStreamChangedRecordModel):
- NewImage: Optional[OrderDynamoDB]
- OldImage: Optional[OrderDynamoDB]
+ NewImage: Optional[OrderDynamoDB] = None
+ OldImage: Optional[OrderDynamoDB] = None
class OrderDynamoDBRecord(DynamoDBStreamRecordModel):
dynamodb: OrderDynamoDBChangeRecord
diff --git a/tests/unit/parser/schemas.py b/tests/unit/parser/schemas.py
index 1da0213ff45..fd2f29697dc 100644
--- a/tests/unit/parser/schemas.py
+++ b/tests/unit/parser/schemas.py
@@ -22,8 +22,8 @@ class MyDynamoBusiness(BaseModel):
class MyDynamoScheme(DynamoDBStreamChangedRecordModel):
- NewImage: Optional[MyDynamoBusiness]
- OldImage: Optional[MyDynamoBusiness]
+ NewImage: Optional[MyDynamoBusiness] = None
+ OldImage: Optional[MyDynamoBusiness] = None
class MyDynamoDBStreamRecordModel(DynamoDBStreamRecordModel):
diff --git a/tests/unit/parser/test_apigw.py b/tests/unit/parser/test_apigw.py
index a65d181cc54..b2ed294ff7a 100644
--- a/tests/unit/parser/test_apigw.py
+++ b/tests/unit/parser/test_apigw.py
@@ -138,7 +138,9 @@ def test_apigw_event_with_invalid_websocket_request():
errors = err.value.errors()
assert len(errors) == 1
expected_msg = "messageId is available only when the `eventType` is `MESSAGE`"
- assert errors[0]["msg"] == expected_msg
+ # Pydantic v2 adds "Value error," to the error string.
+ # So to maintain compatibility with v1 and v2, we've changed the way we test this.
+ assert expected_msg in errors[0]["msg"]
assert expected_msg in str(err.value)
diff --git a/tests/unit/parser/test_cloudwatch.py b/tests/unit/parser/test_cloudwatch.py
index bc8bf0776f9..48d296c40ef 100644
--- a/tests/unit/parser/test_cloudwatch.py
+++ b/tests/unit/parser/test_cloudwatch.py
@@ -86,7 +86,9 @@ def test_handle_invalid_cloudwatch_trigger_event_no_envelope():
with pytest.raises(ValidationError) as context:
CloudWatchLogsModel(**raw_event)
- assert context.value.errors()[0]["msg"] == "unable to decompress data"
+ # Pydantic v2 adds "Value error," to the error string.
+ # So to maintain compatibility with v1 and v2, we've changed the way we test this.
+ assert "unable to decompress data" in context.value.errors()[0]["msg"]
def test_handle_invalid_event_with_envelope():