Skip to content

Commit a44bd6d

Browse files
anafalcaoAna Falcaoleandrodamascena
authored
fix(parser): remove AttributeError validation from event_parser function (#5742)
* add pydatinc validationError * removed unnecessary exc from exceptions Signed-off-by: Ana Falcão <[email protected]> * add two more validation error tests * add more specific error tests * remove one specific error test * fix doc and add more test * change funct doc and more tests * remove unused import * change error message * remove exceptions not needed * remove error handling from decorator * revert from the original file * revert another test --------- Signed-off-by: Ana Falcão <[email protected]> Co-authored-by: Ana Falcao <[email protected]> Co-authored-by: Leandro Damascena <[email protected]>
1 parent aded1a9 commit a44bd6d

File tree

2 files changed

+82
-17
lines changed

2 files changed

+82
-17
lines changed

aws_lambda_powertools/utilities/parser/parser.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ def handler(event: Order, context: LambdaContext):
8484
Raises
8585
------
8686
ValidationError
87-
When input event does not conform with model provided
87+
When input event does not conform with the provided model
8888
InvalidModelTypeError
89-
When model given does not implement BaseModel or is not provided
89+
When the model given does not implement BaseModel, is not provided
9090
InvalidEnvelopeError
9191
When envelope given does not implement BaseEnvelope
9292
"""
@@ -103,17 +103,14 @@ def handler(event: Order, context: LambdaContext):
103103
"or as the type hint of `event` in the handler that it wraps",
104104
)
105105

106-
try:
107-
if envelope:
108-
parsed_event = parse(event=event, model=model, envelope=envelope)
109-
else:
110-
parsed_event = parse(event=event, model=model)
111-
112-
logger.debug(f"Calling handler {handler.__name__}")
113-
return handler(parsed_event, context, **kwargs)
114-
except AttributeError as exc:
115-
raise InvalidModelTypeError(f"Error: {str(exc)}. Please ensure the type you're trying to parse into is correct")
106+
if envelope:
107+
parsed_event = parse(event=event, model=model, envelope=envelope)
108+
else:
109+
parsed_event = parse(event=event, model=model)
116110

111+
logger.debug(f"Calling handler {handler.__name__}")
112+
return handler(parsed_event, context, **kwargs)
113+
117114

118115
@overload
119116
def parse(event: dict[str, Any], model: type[T]) -> T: ... # pragma: no cover
@@ -192,6 +189,7 @@ def handler(event: Order, context: LambdaContext):
192189
adapter = _retrieve_or_set_model_from_cache(model=model)
193190

194191
logger.debug("Parsing and validating event model; no envelope used")
192+
195193
return _parse_and_validate_event(data=event, adapter=adapter)
196194

197195
# Pydantic raises PydanticSchemaGenerationError when the model is not a Pydantic model
@@ -204,4 +202,4 @@ def handler(event: Order, context: LambdaContext):
204202
f"Error: {str(exc)}. Please ensure the Input model inherits from BaseModel,\n"
205203
"and your payload adheres to the specified Input model structure.\n"
206204
f"Model={model}",
207-
) from exc
205+
) from exc

tests/functional/parser/test_parser.py

+71-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import pydantic
66
import pytest
7-
from pydantic import ValidationError
87
from typing_extensions import Annotated
8+
from pydantic import ValidationError, BaseModel
99

1010
from aws_lambda_powertools.utilities.parser import event_parser, exceptions, parse
1111
from aws_lambda_powertools.utilities.parser.envelopes.sqs import SqsEnvelope
@@ -130,6 +130,68 @@ def handler(event, _):
130130
with pytest.raises(ValidationError):
131131
handler({"project": "powertools"}, LambdaContext())
132132

133+
def test_parser_validation_error():
134+
class StrictModel(pydantic.BaseModel):
135+
age: int
136+
name: str
137+
138+
@event_parser(model=StrictModel)
139+
def handle_validation(event: Dict, _: LambdaContext):
140+
return event
141+
142+
invalid_event = {"age": "not_a_number", "name": 123} # intentionally wrong types
143+
144+
with pytest.raises(ValidationError) as exc_info:
145+
handle_validation(event=invalid_event, context=LambdaContext())
146+
147+
assert "age" in str(exc_info.value) # Verify the error mentions the invalid field
148+
149+
def test_parser_type_value_errors():
150+
class CustomModel(pydantic.BaseModel):
151+
timestamp: datetime
152+
status: Literal["SUCCESS", "FAILURE"]
153+
154+
@event_parser(model=CustomModel)
155+
def handle_type_validation(event: Dict, _: LambdaContext):
156+
return event
157+
158+
# Test both TypeError and ValueError scenarios
159+
invalid_events = [
160+
{"timestamp": "invalid-date", "status": "SUCCESS"}, # Will raise ValueError for invalid date
161+
{"timestamp": datetime.now(), "status": "INVALID"} # Will raise ValueError for invalid literal
162+
]
163+
164+
for invalid_event in invalid_events:
165+
with pytest.raises((TypeError, ValueError)):
166+
handle_type_validation(event=invalid_event, context=LambdaContext())
167+
168+
169+
def test_event_parser_no_model():
170+
with pytest.raises(exceptions.InvalidModelTypeError):
171+
@event_parser
172+
def handler(event, _):
173+
return event
174+
175+
handler({}, None)
176+
177+
178+
class Shopping(BaseModel):
179+
id: int
180+
description: str
181+
182+
def test_event_parser_invalid_event():
183+
event = {"id": "forgot-the-id", "description": "really nice blouse"} # 'id' is invalid
184+
185+
@event_parser(model=Shopping)
186+
def handler(event, _):
187+
return event
188+
189+
with pytest.raises(ValidationError):
190+
handler(event, None)
191+
192+
with pytest.raises(ValidationError):
193+
parse(event, model=Shopping)
194+
133195

134196
@pytest.mark.parametrize(
135197
"test_input,expected",
@@ -138,7 +200,10 @@ def handler(event, _):
138200
{"status": "succeeded", "name": "Clifford", "breed": "Labrador"},
139201
"Successfully retrieved Labrador named Clifford",
140202
),
141-
({"status": "failed", "error": "oh some error"}, "Uh oh. Had a problem: oh some error"),
203+
(
204+
{"status": "failed", "error": "oh some error"},
205+
"Uh oh. Had a problem: oh some error",
206+
),
142207
],
143208
)
144209
def test_parser_unions(test_input, expected):
@@ -163,15 +228,17 @@ def handler(event, _: Any) -> str:
163228
ret = handler(test_input, None)
164229
assert ret == expected
165230

166-
167231
@pytest.mark.parametrize(
168232
"test_input,expected",
169233
[
170234
(
171235
{"status": "succeeded", "name": "Clifford", "breed": "Labrador"},
172236
"Successfully retrieved Labrador named Clifford",
173237
),
174-
({"status": "failed", "error": "oh some error"}, "Uh oh. Had a problem: oh some error"),
238+
(
239+
{"status": "failed", "error": "oh some error"},
240+
"Uh oh. Had a problem: oh some error",
241+
),
175242
],
176243
)
177244
def test_parser_unions_with_type_adapter_instance(test_input, expected):

0 commit comments

Comments
 (0)