Closed
Description
If a route function returns a dict containing an Enum, the json encoder will fail.
Expected Behavior
Since the Response was not returned, API Gateway event handler will create one. It calls json.dumps to serialize the dict using its own encoder.
The expected json output would transform the Enum in its value.
Current Behavior
The current behavior is that the encoder throws a TypeError saying Enum is not serializable.
Possible Solution
My suggestion is to check if the object is an Enum and output its value.
The example bellow handle Enum and also a set
class CustomEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, Enum):
return o.value
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
For now the workaround is to always create a Response and call json.dumps with my own custom encoder
Steps to Reproduce (for bugs)
Try the following code
from enum import Enum
class Color(Enum):
RED =1
BLUE = 2
@app.get("/colors")
def get_color() -> Dict:
return {
"color": Color.RED,
"variations": {"light", "dark"}
}
Environment
Lambda Power Tools version 1.17.1
Running with PyTest
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/tracing/tracer.py:313: in decorate
response = lambda_handler(event, context, **kwargs)
_admin_controller.py:273: in handler
return app.resolve(event, context)
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/event_handler/api_gateway.py:418: in resolve
return self._resolve().build(self.current_event, self._cors)
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/event_handler/api_gateway.py:450: in _resolve
return self._call_route(route, match.groupdict())
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/event_handler/api_gateway.py:478: in _call_route
return ResponseBuilder(self._to_response(route.func(**args)), route)
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/event_handler/api_gateway.py:497: in _to_response
body=json.dumps(result, separators=(",", ":"), cls=Encoder),
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/__init__.py:234: in dumps
return cls(
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py:199: in encode
chunks = self.iterencode(o, _one_shot=True)
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py:257: in iterencode
return _iterencode(o, 0)
../../../../Library/Caches/pypoetry/virtualenvs/te-admin-api-y-cvF00h-py3.8/lib/python3.8/site-packages/aws_lambda_powertools/shared/json_encoder.py:16: in default
return super().default(obj)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <aws_lambda_powertools.shared.json_encoder.Encoder object at 0x7f915fbaea90>
o = <Region.CENTRAL: 6>
def default(self, o):
"""Implement this method in a subclass such that it returns
a serializable object for ``o``, or calls the base implementation
(to raise a ``TypeError``).
For example, to support arbitrary iterators, you could
implement default like this::
def default(self, o):
try:
iterable = iter(o)
except TypeError:
pass
else:
return list(iterable)
# Let the base class default method raise the TypeError
return JSONEncoder.default(self, o)
"""
> raise TypeError(f'Object of type {o.__class__.__name__} '
f'is not JSON serializable')
E TypeError: Object of type Region is not JSON serializable
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py:179: TypeError