From 87b1caba128509a4ab0d1d926c80fea293145b26 Mon Sep 17 00:00:00 2001 From: walmsles <2704782+walmsles@users.noreply.github.com> Date: Fri, 21 Apr 2023 22:28:03 +1000 Subject: [PATCH 1/3] fix(batch): resolve use of ValidationError in bacth which breaks optional pydantic dependency --- aws_lambda_powertools/utilities/batch/base.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/aws_lambda_powertools/utilities/batch/base.py b/aws_lambda_powertools/utilities/batch/base.py index 210caf2bb14..a308399fcf2 100644 --- a/aws_lambda_powertools/utilities/batch/base.py +++ b/aws_lambda_powertools/utilities/batch/base.py @@ -26,7 +26,6 @@ KinesisStreamRecord, ) from aws_lambda_powertools.utilities.data_classes.sqs_event import SQSRecord -from aws_lambda_powertools.utilities.parser import ValidationError from aws_lambda_powertools.utilities.typing import LambdaContext logger = logging.getLogger(__name__) @@ -496,9 +495,10 @@ def _process_record(self, record: dict) -> Union[SuccessResponse, FailureRespons result = self.handler(record=data) return self.success_handler(record=record, result=result) - except ValidationError: - return self._register_model_validation_error_record(record) - except Exception: + except Exception as exc: + if exc.__class__.__name__ == "ValidationError": + return self._register_model_validation_error_record(record) + return self.failure_handler(record=data, exception=sys.exc_info()) @@ -634,7 +634,8 @@ async def _async_process_record(self, record: dict) -> Union[SuccessResponse, Fa result = await self.handler(record=data) return self.success_handler(record=record, result=result) - except ValidationError: - return self._register_model_validation_error_record(record) - except Exception: + except Exception as exc: + if exc.__class__.__name__ == "ValidationError": + return self._register_model_validation_error_record(record) + return self.failure_handler(record=data, exception=sys.exc_info()) From ab170fbef2aa23381e22c610bd1adb28635fef2a Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 21 Apr 2023 14:52:57 +0200 Subject: [PATCH 2/3] refactor: use pydantic contract and triple check it comes from the same model we know --- aws_lambda_powertools/utilities/batch/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/batch/base.py b/aws_lambda_powertools/utilities/batch/base.py index a308399fcf2..5d1b47b318b 100644 --- a/aws_lambda_powertools/utilities/batch/base.py +++ b/aws_lambda_powertools/utilities/batch/base.py @@ -496,7 +496,12 @@ def _process_record(self, record: dict) -> Union[SuccessResponse, FailureRespons return self.success_handler(record=record, result=result) except Exception as exc: - if exc.__class__.__name__ == "ValidationError": + # NOTE: Pydantic is an optional dependency, but when used and a poison pill scenario happens + # 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: return self._register_model_validation_error_record(record) return self.failure_handler(record=data, exception=sys.exc_info()) From 78284f72371eb8cc5f4b9e55a708a8c298679039 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 21 Apr 2023 14:56:55 +0200 Subject: [PATCH 3/3] chore: duplicate fix in async --- aws_lambda_powertools/utilities/batch/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/batch/base.py b/aws_lambda_powertools/utilities/batch/base.py index 5d1b47b318b..c2c91708272 100644 --- a/aws_lambda_powertools/utilities/batch/base.py +++ b/aws_lambda_powertools/utilities/batch/base.py @@ -640,7 +640,12 @@ async def _async_process_record(self, record: dict) -> Union[SuccessResponse, Fa return self.success_handler(record=record, result=result) except Exception as exc: - if exc.__class__.__name__ == "ValidationError": + # NOTE: Pydantic is an optional dependency, but when used and a poison pill scenario happens + # 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: return self._register_model_validation_error_record(record) return self.failure_handler(record=data, exception=sys.exc_info())