Skip to content

Commit 0ab103c

Browse files
committed
Merge branch 'develop' of https://github.com/awslabs/aws-lambda-powertools-python into feature/905-datetime
* 'develop' of https://github.com/awslabs/aws-lambda-powertools-python: feat(feature_flags): support beyond boolean values (JSON values) (aws-powertools#804) docs: consistency around admonitions and snippets (aws-powertools#919) chore(deps-dev): bump mypy from 0.920 to 0.930 (aws-powertools#925) fix(event-sources): handle dynamodb null type as none, not bool (aws-powertools#929) fix(apigateway): support @app.not_found() syntax & housekeeping (aws-powertools#926) docs: Added GraphQL Sample API to Examples section of README.md (aws-powertools#930) feat(idempotency): support dataclasses & pydantic models payloads (aws-powertools#908) feat(tracer): ignore tracing for certain hostname(s) or url(s) (aws-powertools#910) feat(event-sources): cache parsed json in data class (aws-powertools#909) fix(warning): future distutils deprecation (aws-powertools#921)
2 parents 00a07d4 + be15e3c commit 0ab103c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2725
-2226
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ With [pip](https://pip.pypa.io/en/latest/index.html) installed, run: ``pip insta
3838
* [Serverless Shopping cart](https://github.com/aws-samples/aws-serverless-shopping-cart)
3939
* [Serverless Airline](https://github.com/aws-samples/aws-serverless-airline-booking)
4040
* [Serverless E-commerce platform](https://github.com/aws-samples/aws-serverless-ecommerce-platform)
41+
* [Serverless GraphQL Nanny Booking Api](https://github.com/trey-rosius/babysitter_api)
4142

4243
## Credits
4344

aws_lambda_powertools/event_handler/api_gateway.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ def _remove_prefix(self, path: str) -> str:
579579
@staticmethod
580580
def _path_starts_with(path: str, prefix: str):
581581
"""Returns true if the `path` starts with a prefix plus a `/`"""
582-
if not isinstance(prefix, str) or len(prefix) == 0:
582+
if not isinstance(prefix, str) or prefix == "":
583583
return False
584584

585585
return path.startswith(prefix + "/")
@@ -633,7 +633,9 @@ def _call_route(self, route: Route, args: Dict[str, str]) -> ResponseBuilder:
633633

634634
raise
635635

636-
def not_found(self, func: Callable):
636+
def not_found(self, func: Optional[Callable] = None):
637+
if func is None:
638+
return self.exception_handler(NotFoundError)
637639
return self.exception_handler(NotFoundError)(func)
638640

639641
def exception_handler(self, exc_class: Type[Exception]):

aws_lambda_powertools/shared/functions.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
1-
from distutils.util import strtobool
21
from typing import Any, Optional, Union
32

43

4+
def strtobool(value: str) -> bool:
5+
"""Convert a string representation of truth to True or False.
6+
7+
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
8+
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
9+
'value' is anything else.
10+
11+
> note:: Copied from distutils.util.
12+
"""
13+
value = value.lower()
14+
if value in ("y", "yes", "t", "true", "on", "1"):
15+
return True
16+
if value in ("n", "no", "f", "false", "off", "0"):
17+
return False
18+
raise ValueError(f"invalid truth value {value!r}")
19+
20+
521
def resolve_truthy_env_var_choice(env: str, choice: Optional[bool] = None) -> bool:
622
"""Pick explicit choice over truthy env value, if available, otherwise return truthy env value
723

aws_lambda_powertools/shared/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
from typing import Any, Callable, TypeVar
1+
from typing import Any, Callable, Dict, List, TypeVar, Union
22

33
AnyCallableT = TypeVar("AnyCallableT", bound=Callable[..., Any]) # noqa: VNE001
4+
# JSON primitives only, mypy doesn't support recursive tho
5+
JSONType = Union[str, int, float, bool, None, Dict[str, Any], List[Any]]

aws_lambda_powertools/tracing/tracer.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
import numbers
77
import os
8-
from typing import Any, Callable, Dict, Optional, Sequence, Union, cast, overload
8+
from typing import Any, Callable, Dict, List, Optional, Sequence, Union, cast, overload
99

1010
from ..shared import constants
1111
from ..shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice
@@ -758,7 +758,7 @@ def _patch_xray_provider(self):
758758
# Due to Lazy Import, we need to activate `core` attrib via import
759759
# we also need to include `patch`, `patch_all` methods
760760
# to ensure patch calls are done via the provider
761-
from aws_xray_sdk.core import xray_recorder
761+
from aws_xray_sdk.core import xray_recorder # type: ignore
762762

763763
provider = xray_recorder
764764
provider.patch = aws_xray_sdk.core.patch
@@ -778,3 +778,27 @@ def _disable_xray_trace_batching(self):
778778

779779
def _is_xray_provider(self):
780780
return "aws_xray_sdk" in self.provider.__module__
781+
782+
def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None):
783+
"""If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being
784+
requested.
785+
786+
> NOTE: If the provider is not xray, nothing will be added to ignore list
787+
788+
Documentation
789+
--------------
790+
- https://github.com/aws/aws-xray-sdk-python#ignoring-httplib-requests
791+
792+
Parameters
793+
----------
794+
hostname : Optional, str
795+
The hostname is matched using the Python fnmatch library which does Unix glob style matching.
796+
urls: Optional, List[str]
797+
List of urls to ignore. Example `tracer.ignore_endpoint(urls=["/ignored-url"])`
798+
"""
799+
if not self._is_xray_provider():
800+
return
801+
802+
from aws_xray_sdk.ext.httplib import add_ignored # type: ignore
803+
804+
add_ignored(hostname=hostname, urls=urls)

aws_lambda_powertools/utilities/data_classes/active_mq_event.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def decoded_data(self) -> str:
2727
@property
2828
def json_data(self) -> Any:
2929
"""Parses the data as json"""
30-
return json.loads(self.decoded_data)
30+
if self._json_data is None:
31+
self._json_data = json.loads(self.decoded_data)
32+
return self._json_data
3133

3234
@property
3335
def connection_id(self) -> str:

aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def user_parameters(self) -> str:
2323
@property
2424
def decoded_user_parameters(self) -> Dict[str, Any]:
2525
"""Json Decoded user parameters"""
26-
return json.loads(self.user_parameters)
26+
if self._json_data is None:
27+
self._json_data = json.loads(self.user_parameters)
28+
return self._json_data
2729

2830

2931
class CodePipelineActionConfiguration(DictWrapper):

aws_lambda_powertools/utilities/data_classes/cognito_user_pool_event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ def session(self) -> List[ChallengeResult]:
687687
@property
688688
def client_metadata(self) -> Optional[Dict[str, str]]:
689689
"""One or more key-value pairs that you can provide as custom input to the Lambda function that you
690-
specify for the create auth challenge trigger.."""
690+
specify for the create auth challenge trigger."""
691691
return self["request"].get("clientMetadata")
692692

693693

aws_lambda_powertools/utilities/data_classes/common.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class DictWrapper:
88

99
def __init__(self, data: Dict[str, Any]):
1010
self._data = data
11+
self._json_data: Optional[Any] = None
1112

1213
def __getitem__(self, key: str) -> Any:
1314
return self._data[key]
@@ -37,7 +38,7 @@ def get_header_value(
3738
name_lower = name.lower()
3839

3940
return next(
40-
# Iterate over the dict and do a case insensitive key comparison
41+
# Iterate over the dict and do a case-insensitive key comparison
4142
(value for key, value in headers.items() if key.lower() == name_lower),
4243
# Default value is returned if no matches was found
4344
default_value,
@@ -65,7 +66,9 @@ def body(self) -> Optional[str]:
6566
@property
6667
def json_body(self) -> Any:
6768
"""Parses the submitted body as json"""
68-
return json.loads(self.decoded_body)
69+
if self._json_data is None:
70+
self._json_data = json.loads(self.decoded_body)
71+
return self._json_data
6972

7073
@property
7174
def decoded_body(self) -> str:
@@ -113,7 +116,7 @@ def get_header_value(
113116
default_value: str, optional
114117
Default value if no value was found by name
115118
case_sensitive: bool
116-
Whether to use a case sensitive look up
119+
Whether to use a case-sensitive look up
117120
Returns
118121
-------
119122
str, optional

aws_lambda_powertools/utilities/data_classes/dynamo_db_stream_event.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,13 @@ def ns_value(self) -> Optional[List[str]]:
106106
return self.get("NS")
107107

108108
@property
109-
def null_value(self) -> Optional[bool]:
109+
def null_value(self) -> None:
110110
"""An attribute of type Null.
111111
112112
Example:
113113
>>> {"NULL": True}
114114
"""
115-
item = self.get("NULL")
116-
return None if item is None else bool(item)
115+
return None
117116

118117
@property
119118
def s_value(self) -> Optional[str]:

0 commit comments

Comments
 (0)