Skip to content

Commit f21b3ca

Browse files
committed
improv: add docstrings; event internal param renamed
1 parent 89122ac commit f21b3ca

File tree

6 files changed

+131
-60
lines changed

6 files changed

+131
-60
lines changed

aws_lambda_powertools/utilities/parser/envelopes/base.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,48 @@ class BaseEnvelope(ABC):
1111
"""ABC implementation for creating a supported Envelope"""
1212

1313
@staticmethod
14-
def _parse(event: Union[Dict[str, Any], str], schema: BaseModel) -> Any:
15-
if event is None:
14+
def _parse(data: Union[Dict[str, Any], str], schema: BaseModel) -> Any:
15+
"""Parses envelope data against schema provided
16+
17+
Parameters
18+
----------
19+
data : Dict
20+
Data to be parsed and validated
21+
schema
22+
Schema to parse and validate data against
23+
24+
Returns
25+
-------
26+
Any
27+
Parsed data
28+
"""
29+
if data is None:
1630
logger.debug("Skipping parsing as event is None")
17-
return event
31+
return data
1832

1933
logger.debug("parsing event against schema")
20-
if isinstance(event, str):
34+
if isinstance(data, str):
2135
logger.debug("parsing event as string")
22-
return schema.parse_raw(event)
36+
return schema.parse_raw(data)
2337

24-
return schema.parse_obj(event)
38+
return schema.parse_obj(data)
2539

2640
@abstractmethod
27-
def parse(self, event: Dict[str, Any], schema: BaseModel):
41+
def parse(self, data: Dict[str, Any], schema: BaseModel):
42+
"""Implementation to parse data against envelope schema, then against the schema
43+
44+
NOTE: Call `_parse` method to fully parse data with schema provided.
45+
46+
Example
47+
-------
48+
49+
**EventBridge envelope implementation example**
50+
51+
def parse(...):
52+
# 1. parses data against envelope schema
53+
parsed_envelope = EventBridgeSchema(**data)
54+
55+
# 2. parses portion of data within the envelope against schema
56+
return self._parse(data=parsed_envelope.detail, schema=schema)
57+
"""
2858
return NotImplemented # pragma: no cover

aws_lambda_powertools/utilities/parser/envelopes/dynamodb.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ class DynamoDBEnvelope(BaseEnvelope):
1717
length of the list is the record's amount in the original event.
1818
"""
1919

20-
def parse(self, event: Dict[str, Any], schema: BaseModel) -> List[Dict[Literal["NewImage", "OldImage"], BaseModel]]:
20+
def parse(self, data: Dict[str, Any], schema: BaseModel) -> List[Dict[Literal["NewImage", "OldImage"], BaseModel]]:
2121
"""Parses DynamoDB Stream records found in either NewImage and OldImage with schema provided
2222
2323
Parameters
2424
----------
25-
event : Dict
25+
data : Dict
2626
Lambda event to be parsed
2727
schema : BaseModel
2828
User schema provided to parse after extracting data using envelope
@@ -32,7 +32,7 @@ def parse(self, event: Dict[str, Any], schema: BaseModel) -> List[Dict[Literal["
3232
List
3333
List of records parsed with schema provided
3434
"""
35-
parsed_envelope = DynamoDBSchema(**event)
35+
parsed_envelope = DynamoDBSchema(**data)
3636
output = []
3737
for record in parsed_envelope.Records:
3838
output.append(

aws_lambda_powertools/utilities/parser/envelopes/event_bridge.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010

1111

1212
class EventBridgeEnvelope(BaseEnvelope):
13-
"""EventBridge envelope to extract data in detail key"""
13+
"""EventBridge envelope to extract data within detail key"""
1414

15-
def parse(self, event: Dict[str, Any], schema: BaseModel) -> BaseModel:
15+
def parse(self, data: Dict[str, Any], schema: BaseModel) -> BaseModel:
1616
"""Parses data found with schema provided
1717
1818
Parameters
1919
----------
20-
event : Dict
20+
data : Dict
2121
Lambda event to be parsed
2222
schema : BaseModel
2323
User schema provided to parse after extracting data using envelope
@@ -27,5 +27,5 @@ def parse(self, event: Dict[str, Any], schema: BaseModel) -> BaseModel:
2727
Any
2828
Parsed detail payload with schema provided
2929
"""
30-
parsed_envelope = EventBridgeSchema(**event)
31-
return self._parse(parsed_envelope.detail, schema)
30+
parsed_envelope = EventBridgeSchema(**data)
31+
return self._parse(data=parsed_envelope.detail, schema=schema)

aws_lambda_powertools/utilities/parser/envelopes/sqs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ class SqsEnvelope(BaseEnvelope):
1919
all items in the list will be parsed as str and npt as JSON (and vice versa)
2020
"""
2121

22-
def parse(self, event: Dict[str, Any], schema: Union[BaseModel, str]) -> List[Union[BaseModel, str]]:
22+
def parse(self, data: Dict[str, Any], schema: Union[BaseModel, str]) -> List[Union[BaseModel, str]]:
2323
"""Parses records found with schema provided
2424
2525
Parameters
2626
----------
27-
event : Dict
27+
data : Dict
2828
Lambda event to be parsed
2929
schema : BaseModel
3030
User schema provided to parse after extracting data using envelope
@@ -34,7 +34,7 @@ def parse(self, event: Dict[str, Any], schema: Union[BaseModel, str]) -> List[Un
3434
List
3535
List of records parsed with schema provided
3636
"""
37-
parsed_envelope = SqsSchema(**event)
37+
parsed_envelope = SqsSchema(**data)
3838
output = []
3939
for record in parsed_envelope.Records:
4040
output.append(self._parse(record.body, schema))

aws_lambda_powertools/utilities/parser/parser.py

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from aws_lambda_powertools.middleware_factory import lambda_handler_decorator
77

8+
from ..typing import LambdaContext
89
from .envelopes.base import BaseEnvelope
910
from .exceptions import InvalidEnvelopeError, InvalidSchemaTypeError, SchemaValidationError
1011

@@ -15,79 +16,119 @@
1516
def parser(
1617
handler: Callable[[Dict, Any], Any],
1718
event: Dict[str, Any],
18-
context: Dict[str, Any],
19+
context: LambdaContext,
1920
schema: BaseModel,
2021
envelope: Optional[BaseEnvelope] = None,
2122
) -> Any:
22-
"""Decorator to conduct advanced parsing & validation for lambda handlers events
23+
"""Lambda handler decorator to parse & validate events using Pydantic models
2324
24-
As Lambda follows (event, context) signature we can remove some of the boilerplate
25-
and also capture any exception any Lambda function throws as metadata.
26-
event will be the parsed and passed as a BaseModel Pydantic class of the input type "schema"
27-
to the lambda handler.
28-
event will be extracted from the envelope in case envelope is not None.
29-
In case envelope is None, the complete event is parsed to match the schema parameter BaseModel definition.
30-
In case envelope is not None, first the event is parsed as the envelope's schema definition, and the user
31-
message is extracted and parsed again as the schema parameter's definition.
25+
It requires a schema that implements Pydantic BaseModel to parse & validate the event.
26+
27+
When an envelope is given, it'll use the following logic:
28+
29+
1. Parse the event against envelope schema first e.g. EnvelopeSchema(**event)
30+
2. Envelope will extract a given key to be parsed against the schema e.g. event.detail
31+
32+
This is useful when you need to confirm event wrapper structure, and
33+
b) selectively extract a portion of your payload for parsing & validation.
34+
35+
NOTE: If envelope is omitted, the complete event is parsed to match the schema parameter BaseModel definition.
3236
3337
Example
3438
-------
35-
**Lambda function using validation decorator**
39+
**Lambda handler decorator to parse & validate event**
40+
41+
class Order(BaseModel):
42+
id: int
43+
description: str
44+
...
45+
46+
@parser(schema=Order)
47+
def handler(event: Order, context: LambdaContext):
48+
...
49+
50+
**Lambda handler decorator to parse & validate event - using built-in envelope**
51+
52+
class Order(BaseModel):
53+
id: int
54+
description: str
55+
...
3656
37-
@parser(schema=MyBusiness, envelope=envelopes.EVENTBRIDGE)
38-
def handler(event: MyBusiness , context: LambdaContext):
57+
@parser(schema=Order, envelope=envelopes.EVENTBRIDGE)
58+
def handler(event: Order, context: LambdaContext):
3959
...
4060
4161
Parameters
4262
----------
43-
handler: input for lambda_handler_decorator, wraps the handler lambda
44-
event: AWS event dictionary
45-
context: AWS lambda context
46-
schema: pydantic BaseModel class. This is the user data schema that will replace the event.
47-
event parameter will be parsed and a new schema object will be created from it.
48-
envelope: what envelope to extract the schema from, can be any AWS service that is currently
49-
supported in the envelopes module. Can be None.
63+
handler: Callable
64+
Method to annotate on
65+
event: Dict
66+
Lambda event to be parsed & validated
67+
context: LambdaContext
68+
Lambda context object
69+
schema: BaseModel
70+
Your data schema that will replace the event.
71+
envelope: BaseEnvelope
72+
Optional envelope to extract the schema from
5073
5174
Raises
5275
------
5376
SchemaValidationError
5477
When input event does not conform with schema provided
5578
InvalidSchemaTypeError
5679
When schema given does not implement BaseModel
80+
InvalidEnvelopeError
81+
When envelope given does not implement BaseEnvelope
5782
"""
5883
parsed_event = parse(event=event, schema=schema, envelope=envelope)
5984
logger.debug(f"Calling handler {handler.__name__}")
6085
return handler(parsed_event, context)
6186

6287

6388
def parse(event: Dict[str, Any], schema: BaseModel, envelope: Optional[BaseEnvelope] = None) -> Any:
64-
"""
65-
Standalone parse function to conduct advanced parsing & validation for lambda handlers events
89+
"""Standalone function to parse & validate events using Pydantic models
6690
67-
As Lambda follows (event, context) signature we can remove some of the boilerplate
68-
and also capture any exception any Lambda function throws as metadata.
69-
event will be the parsed and passed as a BaseModel Pydantic class of the input type "schema"
70-
to the lambda handler.
71-
event will be extracted from the envelope in case envelope is not None.
72-
In case envelope is None, the complete event is parsed to match the schema parameter BaseModel definition.
73-
In case envelope is not None, first the event is parsed as the envelope's schema definition, and the user
74-
message is extracted and parsed again as the schema parameter's definition.
91+
Typically used when you need fine-grained control over error handling compared to parser decorator.
7592
7693
Example
7794
-------
78-
**Lambda function using standalone parse**
7995
80-
def handler(event: MyBusiness , context: LambdaContext):
81-
parse(event=event, schema=MyBusiness, envelope=envelopes.EVENTBRIDGE)
96+
**Lambda handler decorator to parse & validate event**
97+
98+
from aws_lambda_powertools.utilities.parser.exceptions import SchemaValidationError
99+
100+
class Order(BaseModel):
101+
id: int
102+
description: str
82103
...
83104
105+
def handler(event: Order, context: LambdaContext):
106+
try:
107+
parse(schema=Order)
108+
except SchemaValidationError:
109+
...
110+
111+
**Lambda handler decorator to parse & validate event - using built-in envelope**
112+
113+
class Order(BaseModel):
114+
id: int
115+
description: str
116+
...
117+
118+
def handler(event: Order, context: LambdaContext):
119+
try:
120+
parse(schema=Order, envelope=envelopes.EVENTBRIDGE)
121+
except SchemaValidationError:
122+
...
123+
84124
Parameters
85125
----------
86-
event: AWS event dictionary
87-
schema: pydantic BaseModel class. This is the user data schema that will replace the event.
88-
event parameter will be parsed and a new schema object will be created from it.
89-
envelope: what envelope to extract the schema from, can be any AWS service that is currently
90-
supported in the envelopes module. Can be None.
126+
event: Dict
127+
Lambda event to be parsed & validated
128+
schema: BaseModel
129+
Your data schema that will replace the event.
130+
envelope: BaseEnvelope
131+
Optional envelope to extract the schema from
91132
92133
Raises
93134
------
@@ -101,7 +142,7 @@ def handler(event: MyBusiness , context: LambdaContext):
101142
if envelope and callable(envelope):
102143
try:
103144
logger.debug(f"Parsing and validating event schema with envelope={envelope}")
104-
return envelope().parse(event=event, schema=schema)
145+
return envelope().parse(data=event, schema=schema)
105146
except AttributeError:
106147
raise InvalidEnvelopeError(f"Envelope must implement BaseEnvelope, envelope={envelope}")
107148
except (ValidationError, TypeError) as e:

tests/functional/parser/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ def dummy_envelope(dummy_envelope_schema):
3737
class MyDummyEnvelope(BaseEnvelope):
3838
"""Unwrap dummy event within payload key"""
3939

40-
def parse(self, event: Dict[str, Any], schema: BaseModel):
40+
def parse(self, data: Dict[str, Any], schema: BaseModel):
4141
try:
42-
parsed_enveloped = dummy_envelope_schema(**event)
42+
parsed_enveloped = dummy_envelope_schema(**data)
4343
except (ValidationError, TypeError) as e:
4444
raise SchemaValidationError("Dummy input does not conform with schema") from e
45-
return self._parse(event=parsed_enveloped.payload, schema=schema)
45+
return self._parse(data=parsed_enveloped.payload, schema=schema)
4646

4747
return MyDummyEnvelope

0 commit comments

Comments
 (0)