Skip to content

Feature request: Return empty Dict or List in Event Source Data Classes instead of None #2605

Closed

Description

@ericbn

Use case

Arguably, it might be easier for users if an empty Dict is returned instead of None for path_parameters in APIGatewayProxyEventV2, for example.

Instead of:

@event_source(data_class=APIGatewayProxyEventV2)
def lambda_handler(event: APIGatewayProxyEventV2, context) -> None:
    parameters = event.path_parameters or {}
    handle(parameters["id"])

it would become:

@event_source(data_class=APIGatewayProxyEventV2)
def lambda_handler(event: APIGatewayProxyEventV2, context) -> None:
    handle(event.path_parameters["id"])

Ultimately this approach could be followed for any return value that is either an Optional[Dict] or an Optional[List], returning an empty Dict or List instead of None in all cases:

  • utilities/data_classes/api_gateway_proxy_event.py
    • APIGatewayEventAuthorizer
      • def claims(self) -> Dict[str, Any]
      • def scopes(self) -> List[str]
    • APIGatewayProxyEvent
      • def multi_value_query_string_parameters(self) -> Dict[str, List[str]]
      • def path_parameters(self) -> Dict[str, str]
      • def stage_variables(self) -> Dict[str, str]
    • RequestContextV2AuthorizerIam
      • def cognito_amr(self) -> List[str]
    • RequestContextV2Authorizer
      • def jwt_claim(self) -> Dict[str, Any]
      • def jwt_scopes(self) -> List[str]
      • def get_lambda(self) -> Dict[str, Any]
    • APIGatewayProxyEventV2
      • def cookies(self) -> List[str]
      • def path_parameters(self) -> Dict[str, str]
      • def stage_variables(self) -> Dict[str, str]

and so on...

The bottom line is: Does it matter for users to differentiate between None and an empty Dict or List for any Event Source Data Classe property? If it does for a particular one, it an exception to a rule or the rule?

This change would be backwards compatible with the previous code.

This approach was already implemented in only one place so far, as far as I could check:

  • utilities/data_classes/common.py
    • BaseProxyEvent
      • def headers(self) -> Dict[str, str]

Solution/User Experience

Solution can be the same as for the headers in the BaseProxyEvent:

@property
def headers(self) -> Dict[str, str]:
return self.get("headers") or {}

Alternative solutions

No response

Acknowledgment

Activity

  1. rubenfonseca commented on Jun 29, 2023

    @rubenfonseca
    Contributor

    Hi @ericbn thank you for sending this proposal! Two notes:

    1. Do you agree that code that backwards compatibility can be ensured since existing code that relies on None as a valid value will still function correctly?
    2. Providing specific use cases and examples where this feature significantly improves the developer experience would strengthen the proposal.

    It seems to be this would be a big breaking change anyways. So we might have to open an RFC to discuss this with the larger community and consider the breaking change for v3.

  2. self-assigned this
    on Jun 29, 2023
  3. added
    need-customer-feedbackRequires more customers feedback before making or revisiting a decision
    revisitMaintainer to provide update or revisit prioritization in the next cycle
    and removed
    triagePending triage from maintainers
    on Jun 29, 2023
  4. moved this from Triage to Pending customer in Powertools for AWS Lambda (Python)on Jun 29, 2023
  5. moved this from Pending customer to On hold in Powertools for AWS Lambda (Python)on Jul 10, 2023
  6. ericbn commented on Jul 11, 2023

    @ericbn
    ContributorAuthor

    Hi. Totally agree this is a breaking change for existing code that relies on None as a valid value. Good point. I see the change in the headers method in BaseProxyEvent was done exactly to keep backward compatibility.

    I propose this change is favor of an API with multiple methods like get_query_string_value and get_header_value in BaseProxyEvent.

    def get_query_string_value(self, name: str, default_value: Optional[str] = None) -> Optional[str]:
    """Get query string value by name
    Parameters
    ----------
    name: str
    Query string parameter name
    default_value: str, optional
    Default value if no value was found by name
    Returns
    -------
    str, optional
    Query string parameter value
    """
    return get_query_string_value(
    query_string_parameters=self.query_string_parameters,
    name=name,
    default_value=default_value,
    )

    # Maintenance: missing @overload to ensure return type is a str when default_value is set
    def get_header_value(
    self,
    name: str,
    default_value: Optional[str] = None,
    case_sensitive: Optional[bool] = False,
    ) -> Optional[str]:
    """Get header value by name
    Parameters
    ----------
    name: str
    Header name
    default_value: str, optional
    Default value if no value was found by name
    case_sensitive: bool
    Whether to use a case-sensitive look up
    Returns
    -------
    str, optional
    Header value
    """
    return get_header_value(
    headers=self.headers,
    name=name,
    default_value=default_value,
    case_sensitive=case_sensitive,
    )

    Pros:

    • No need to create extra methods in the data classes.
    • User can use the List and Dict API directly, for example to choose between event.query_string_parameters["foo"] or event.query_string_parameters.get("foo") or event.query_string_parameters.get("foo", "default_foo") in case the key is required or not, or to use other functions like first(event.cookies).

    Cons(?):

    • As the List and Dict APIs are used directly, a parameter like case_sensitive in get_header_value would have to be replaced by returning a custom CaseInsensitiveDict.
  7. leandrodamascena commented on Aug 17, 2023

    @leandrodamascena
    Contributor

    Hello @ericbn! We've updated our roadmap and included this feature request. We are planning to release this feature in Powertools v3.

    Roadmap for v3: https://docs.powertools.aws.dev/lambda/python/latest/roadmap/#v3

  8. 3 remaining items

  9. added
    v3Features that will be included in Powertools v3.
    on Aug 20, 2023
  10. ericbn commented on Aug 23, 2023

    @ericbn
    ContributorAuthor

    Sure, I’ll wait. Makes perfect sense. Looking forward for my first contribution with a PR to the project!

  11. moved this from Backlog to Pending customer in Powertools for AWS Lambda (Python)on Dec 6, 2023
  12. heitorlessa commented on Jun 10, 2024

    @heitorlessa
    Contributor

    Took a long time (stability/backwards compatibility) but we've started implementing for v3: #4189

  13. moved this from Pending customer to Working on it in Powertools for AWS Lambda (Python)on Jun 10, 2024
  14. ericbn commented on Jun 10, 2024

    @ericbn
    ContributorAuthor

    Oi @heitorlessa. I'll be happy to work on this as a contribution to the project.

  15. leandrodamascena commented on Jul 29, 2024

    @leandrodamascena
    Contributor

    Closed via #4606

  16. github-actions commented on Jul 29, 2024

    @github-actions
    Contributor

    ⚠️COMMENT VISIBILITY WARNING⚠️

    This issue is now closed. Please be mindful that future comments are hard for our team to see.

    If you need more assistance, please either tag a team member or open a new issue that references this one.

    If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking-changeBreaking changefeature-requestfeature requesthelp wantedCould use a second pair of eyes/handsneed-customer-feedbackRequires more customers feedback before making or revisiting a decisionneed-rfcrevisitMaintainer to provide update or revisit prioritization in the next cyclev3Features that will be included in Powertools v3.

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions