Skip to content

feat(data_classes): return empty dict or list instead of None #4606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add custom CaseInsensitiveDict
This is hopefully a simpler implementations that the requests' package
one, but still had to be minimally complex to be complete.
  • Loading branch information
ericbn committed Jul 4, 2024
commit c4d2050cf332e6564468b9570cf788a87f2a1e7e
9 changes: 4 additions & 5 deletions aws_lambda_powertools/utilities/data_classes/alb_event.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from typing import Any, Dict, List, MutableMapping

from requests.structures import CaseInsensitiveDict
from typing import Any, Dict, List

from aws_lambda_powertools.shared.headers_serializer import (
BaseHeadersSerializer,
Expand All @@ -9,6 +7,7 @@
)
from aws_lambda_powertools.utilities.data_classes.common import (
BaseProxyEvent,
CaseInsensitiveDict,
DictWrapper,
)

Expand Down Expand Up @@ -42,11 +41,11 @@ def resolved_query_string_parameters(self) -> Dict[str, List[str]]:
return self.multi_value_query_string_parameters or super().resolved_query_string_parameters

@property
def multi_value_headers(self) -> MutableMapping[str, List[str]]:
def multi_value_headers(self) -> Dict[str, List[str]]:
return CaseInsensitiveDict(self.get("multiValueHeaders"))

@property
def resolved_headers_field(self) -> MutableMapping[str, Any]:
def resolved_headers_field(self) -> Dict[str, Any]:
return self.multi_value_headers or self.headers

def header_serializer(self) -> BaseHeadersSerializer:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import enum
import re
from typing import Any, Dict, List, MutableMapping, Optional

from requests.structures import CaseInsensitiveDict
from typing import Any, Dict, List, Optional

from aws_lambda_powertools.utilities.data_classes.common import (
BaseRequestContext,
BaseRequestContextV2,
CaseInsensitiveDict,
DictWrapper,
)

Expand Down Expand Up @@ -142,7 +141,7 @@ def http_method(self) -> str:
return self["httpMethod"]

@property
def headers(self) -> MutableMapping[str, str]:
def headers(self) -> Dict[str, str]:
return CaseInsensitiveDict(self["headers"])

@property
Expand Down Expand Up @@ -223,7 +222,7 @@ def cookies(self) -> List[str]:
return self["cookies"]

@property
def headers(self) -> MutableMapping[str, str]:
def headers(self) -> Dict[str, str]:
"""Http headers"""
return CaseInsensitiveDict(self["headers"])

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from functools import cached_property
from typing import Any, Dict, List, MutableMapping, Optional

from requests.structures import CaseInsensitiveDict
from typing import Any, Dict, List, Optional

from aws_lambda_powertools.shared.headers_serializer import (
BaseHeadersSerializer,
Expand All @@ -12,6 +10,7 @@
BaseProxyEvent,
BaseRequestContext,
BaseRequestContextV2,
CaseInsensitiveDict,
DictWrapper,
)

Expand Down Expand Up @@ -115,7 +114,7 @@ def resource(self) -> str:
return self["resource"]

@property
def multi_value_headers(self) -> MutableMapping[str, List[str]]:
def multi_value_headers(self) -> Dict[str, List[str]]:
return CaseInsensitiveDict(self.get("multiValueHeaders"))

@property
Expand All @@ -130,7 +129,7 @@ def resolved_query_string_parameters(self) -> Dict[str, List[str]]:
return super().resolved_query_string_parameters

@property
def resolved_headers_field(self) -> MutableMapping[str, Any]:
def resolved_headers_field(self) -> Dict[str, Any]:
return self.multi_value_headers or self.headers

@property
Expand Down Expand Up @@ -316,5 +315,5 @@ def header_serializer(self):
return HttpApiHeadersSerializer()

@cached_property
def resolved_headers_field(self) -> MutableMapping[str, Any]:
return CaseInsensitiveDict({k: v.split(",") if "," in v else v for k, v in self.headers.items()})
def resolved_headers_field(self) -> Dict[str, Any]:
return CaseInsensitiveDict((k, v.split(",") if "," in v else v) for k, v in self.headers.items())
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from typing import Any, Dict, List, MutableMapping, Optional, Union
from typing import Any, Dict, List, Optional, Union

from requests.structures import CaseInsensitiveDict

from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict, DictWrapper


def get_identity_object(identity: Optional[dict]) -> Any:
Expand Down Expand Up @@ -188,7 +186,7 @@ def source(self) -> Dict[str, Any]:
return self.get("source") or {}

@property
def request_headers(self) -> MutableMapping[str, str]:
def request_headers(self) -> Dict[str, str]:
"""Request headers"""
return CaseInsensitiveDict(self["request"]["headers"])

Expand Down
52 changes: 47 additions & 5 deletions aws_lambda_powertools/utilities/data_classes/common.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
import base64
import json
from functools import cached_property
from typing import Any, Callable, Dict, Iterator, List, Mapping, MutableMapping, Optional

from requests.structures import CaseInsensitiveDict
from typing import Any, Callable, Dict, Iterator, List, Mapping, Optional

from aws_lambda_powertools.shared.headers_serializer import BaseHeadersSerializer


class CaseInsensitiveDict(dict):
"""Case insensitive dict implementation. Assumes string keys only."""

def __init__(self, data=None, **kwargs):
super().__init__()
self.update(data, **kwargs)

def get(self, k, default=None):
return super().get(k.lower(), default)

def pop(self, k):
return super().pop(k.lower())

def setdefault(self, k, default=None):
return super().setdefault(k.lower(), default)

def update(self, data=None, **kwargs):
if data is not None:
if isinstance(data, Mapping):
super().update((k.lower(), v) for k, v in data.items())
else:
super().update((k.lower(), v) for k, v in data)
super().update((k.lower(), v) for k, v in kwargs)

def __contains__(self, k):
return super().__contains__(k.lower())

def __delitem__(self, k):
super().__delitem__(k.lower())

def __eq__(self, other):
if not isinstance(other, Mapping):
return False
if not isinstance(other, CaseInsensitiveDict):
other = CaseInsensitiveDict(other)
return super().__eq__(other)

def __getitem__(self, k):
return super().__getitem__(k.lower())

def __setitem__(self, k, v):
super().__setitem__(k.lower(), v)


class DictWrapper(Mapping):
"""Provides a single read only access to a wrapper dict"""

Expand Down Expand Up @@ -93,7 +135,7 @@ def raw_event(self) -> Dict[str, Any]:

class BaseProxyEvent(DictWrapper):
@property
def headers(self) -> MutableMapping[str, str]:
def headers(self) -> Dict[str, str]:
return CaseInsensitiveDict(self.get("headers"))

@property
Expand All @@ -116,7 +158,7 @@ def resolved_query_string_parameters(self) -> Dict[str, List[str]]:
return {k: v.split(",") for k, v in self.query_string_parameters.items()}

@property
def resolved_headers_field(self) -> MutableMapping[str, str]:
def resolved_headers_field(self) -> Dict[str, str]:
"""
This property determines the appropriate header to be used
as a trusted source for validating OpenAPI.
Expand Down
10 changes: 4 additions & 6 deletions aws_lambda_powertools/utilities/data_classes/kafka_event.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import base64
from functools import cached_property
from typing import Any, Dict, Iterator, List, MutableMapping, Optional
from typing import Any, Dict, Iterator, List, Optional

from requests.structures import CaseInsensitiveDict

from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict, DictWrapper


class KafkaEventRecord(DictWrapper):
Expand Down Expand Up @@ -64,9 +62,9 @@ def headers(self) -> List[Dict[str, List[int]]]:
return self["headers"]

@cached_property
def decoded_headers(self) -> MutableMapping[str, bytes]:
def decoded_headers(self) -> Dict[str, bytes]:
"""Decodes the headers as a single dictionary."""
return CaseInsensitiveDict({k: bytes(v) for chunk in self.headers for k, v in chunk.items()})
return CaseInsensitiveDict((k, bytes(v)) for chunk in self.headers for k, v in chunk.items())


class KafkaEvent(DictWrapper):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from typing import MutableMapping, Optional
from typing import Dict, Optional

from requests.structures import CaseInsensitiveDict

from aws_lambda_powertools.utilities.data_classes.common import DictWrapper
from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict, DictWrapper


class S3ObjectContext(DictWrapper):
Expand Down Expand Up @@ -64,7 +62,7 @@ def url(self) -> str:
return self["url"]

@property
def headers(self) -> MutableMapping[str, str]:
def headers(self) -> Dict[str, str]:
"""A map of string to strings containing the HTTP headers and their values from the original call,
excluding any authorization-related headers.

Expand Down
16 changes: 9 additions & 7 deletions aws_lambda_powertools/utilities/data_classes/vpc_lattice.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from functools import cached_property
from typing import Any, Dict, MutableMapping, Optional

from requests.structures import CaseInsensitiveDict
from typing import Any, Dict, Optional

from aws_lambda_powertools.shared.headers_serializer import (
BaseHeadersSerializer,
HttpApiHeadersSerializer,
)
from aws_lambda_powertools.utilities.data_classes.common import BaseProxyEvent, DictWrapper
from aws_lambda_powertools.utilities.data_classes.common import (
BaseProxyEvent,
CaseInsensitiveDict,
DictWrapper,
)
from aws_lambda_powertools.utilities.data_classes.shared_functions import base64_decode


Expand All @@ -23,7 +25,7 @@ def json_body(self) -> Any:
return self._json_deserializer(self.decoded_body)

@property
def headers(self) -> MutableMapping[str, str]:
def headers(self) -> Dict[str, str]:
"""The VPC Lattice event headers."""
return CaseInsensitiveDict(self["headers"])

Expand Down Expand Up @@ -73,8 +75,8 @@ def query_string_parameters(self) -> Dict[str, str]:
return self["query_string_parameters"]

@cached_property
def resolved_headers_field(self) -> MutableMapping[str, Any]:
return CaseInsensitiveDict({k: v.split(",") if "," in v else v for k, v in self.headers.items()})
def resolved_headers_field(self) -> Dict[str, Any]:
return CaseInsensitiveDict((k, v.split(",") if "," in v else v) for k, v in self.headers.items())


class vpcLatticeEventV2Identity(DictWrapper):
Expand Down