in hasher-matcher-actioner/hmalib/common/aws_dataclass.py [0:0]
def aws_to_py(in_type: t.Type[T], aws_field: t.Any) -> T:
"""
Convert an AWS item back into its py equivalent
This might not even be strictly required, but we check that
all the types are roughly what we expect, and convert
Decimals back into ints/floats
"""
origin = t.get_origin(in_type)
args = t.get_args(in_type)
check_type = origin
if in_type is float:
check_type = Decimal
elif in_type is int:
check_type = (int, Decimal)
elif is_dataclass(in_type):
check_type = dict
elif check_type is set and args:
if args[0] not in (str, float, int, Decimal):
check_type = list
if not isinstance(aws_field, check_type or in_type):
# If you are getting random deserialization errors in tests that you did
# not touch, have a look at
# https://github.com/facebook/ThreatExchange/issues/697
raise AWSSerializationFailure(
"Deserialization error: "
f"Expected {in_type} got {type(aws_field)} ({aws_field!r})"
)
if in_type is int: # N
return int(aws_field) # type: ignore # mypy/issues/10003
if in_type is float: # N
return float(aws_field) # type: ignore # mypy/issues/10003
if in_type is Decimal: # N
return aws_field # type: ignore # mypy/issues/10003
if in_type is str: # S
return aws_field # type: ignore # mypy/issues/10003
if in_type is bool: # BOOL
return aws_field # type: ignore # mypy/issues/10003
if in_type is t.Set[str]: # SS
return aws_field # type: ignore # mypy/issues/10003
if in_type is t.Set[int]: # SN
return {int(s) for s in aws_field} # type: ignore # mypy/issues/10003
if in_type is t.Set[float]: # SN
return {float(s) for s in aws_field} # type: ignore # mypy/issues/10003
if origin is set: # L - special case
return {aws_to_py(args[0], v) for v in aws_field} # type: ignore # mypy/issues/10003
if origin is list: # L
return [aws_to_py(args[0], v) for v in aws_field] # type: ignore # mypy/issues/10003
# It would be possible to add support for nested dataclasses here, which
# just become maps with the keys as their attributes
# Another option would be adding a new class that adds methods to convert
# to an AWS-friendly struct and back
if origin is dict and args[0] is str: # M
# check if value type of map origin is explicitly set
return {k: aws_to_py(args[1], v) for k, v in aws_field.items()} # type: ignore # mypy/issues/10003
if is_dataclass(in_type):
kwargs = {}
for field in fields(in_type):
if not field.init:
continue
val = aws_field.get(field.name)
if val is None:
continue # Hopefully missing b/c default or version difference
kwargs[field.name] = aws_to_py(field.type, val)
return in_type(**kwargs) # type: ignore # No idea how to correctly type this
raise AWSSerializationFailure(f"Missing deserialization logic for {in_type!r}")