Skip to content

Commit d933735

Browse files
committed
chore(common): reusable function to extract event from models
1 parent 168a4df commit d933735

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

aws_lambda_powertools/shared/functions.py

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from __future__ import annotations
2+
13
import base64
4+
import dataclasses
25
import itertools
36
import logging
47
import os
58
import warnings
69
from binascii import Error as BinAsciiError
7-
from typing import Dict, Generator, Optional, Union, overload
10+
from typing import Any, Dict, Generator, Optional, Union, overload
811

912
from aws_lambda_powertools.shared import constants
1013

@@ -121,3 +124,43 @@ def powertools_debug_is_set() -> bool:
121124
def slice_dictionary(data: Dict, chunk_size: int) -> Generator[Dict, None, None]:
122125
for _ in range(0, len(data), chunk_size):
123126
yield {dict_key: data[dict_key] for dict_key in itertools.islice(data, chunk_size)}
127+
128+
129+
def extract_event_from_common_models(data: Any) -> Dict | Any:
130+
"""Extract raw event from common types used in Powertools
131+
132+
If event cannot be extracted, return received data as is.
133+
134+
Common models:
135+
136+
- Event Source Data Classes (DictWrapper)
137+
- Python Dataclasses
138+
- Pydantic Models (BaseModel)
139+
140+
Parameters
141+
----------
142+
data : Any
143+
Original event, a potential instance of DictWrapper/BaseModel/Dataclass
144+
145+
Notes
146+
-----
147+
148+
Why not using static type for function argument?
149+
150+
DictWrapper would cause a circular import. Pydantic BaseModel could
151+
cause a ModuleNotFound or trigger init reflection worsening cold start.
152+
"""
153+
# Short-circuit most common type first for perf
154+
if isinstance(data, dict):
155+
return data
156+
157+
# Is it an Event Source Data Class?
158+
if getattr(data, "raw_event", None):
159+
return data.raw_event
160+
161+
# Is it a Pydantic Model?
162+
if callable(getattr(data, "dict", None)):
163+
return data.dict()
164+
165+
# Is it a Dataclass? If not return as is
166+
return dataclasses.asdict(data) if dataclasses.is_dataclass(data) else data

tests/functional/test_shared_functions.py

+39
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import warnings
2+
from dataclasses import dataclass
23

34
import pytest
5+
from pydantic import BaseModel
46

57
from aws_lambda_powertools.shared import constants
68
from aws_lambda_powertools.shared.functions import (
9+
extract_event_from_common_models,
710
powertools_debug_is_set,
811
powertools_dev_is_set,
912
resolve_env_var_choice,
1013
resolve_truthy_env_var_choice,
1114
strtobool,
1215
)
16+
from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
1317

1418

1519
def test_resolve_env_var_choice_explicit_wins_over_env_var():
@@ -64,3 +68,38 @@ def test_powertools_debug_warning(monkeypatch: pytest.MonkeyPatch):
6468
powertools_debug_is_set()
6569
assert len(w) == 1
6670
assert str(w[0].message) == warning_message
71+
72+
73+
def test_extract_event_dict():
74+
data = {"hello": "world"}
75+
assert extract_event_from_common_models(data) == data
76+
77+
78+
def test_extract_event_pydantic():
79+
class DummyModel(BaseModel):
80+
hello: str
81+
82+
data = {"hello": "world"}
83+
assert extract_event_from_common_models(DummyModel(**data)) == data
84+
85+
86+
def test_extract_event_dict_wrapper():
87+
class DummyClassSample(DictWrapper):
88+
pass
89+
90+
data = {"hello": "world"}
91+
assert extract_event_from_common_models(DummyClassSample(data)) == data
92+
93+
94+
def test_extract_event_dataclass():
95+
@dataclass
96+
class DummyDataclass:
97+
hello: str
98+
99+
data = {"hello": "world"}
100+
assert extract_event_from_common_models(DummyDataclass(**data)) == data
101+
102+
103+
@pytest.mark.parametrize("data", [False, True, "", 10, [], {}, object])
104+
def test_extract_event_any(data):
105+
assert extract_event_from_common_models(data) == data

0 commit comments

Comments
 (0)