Skip to content

Commit 7104eb8

Browse files
Merge branch 'develop' into fix/get-app-config-provider-key
2 parents 7f29554 + 46eb971 commit 7104eb8

44 files changed

Lines changed: 1754 additions & 732 deletions

File tree

Some content is hidden

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

‎CHANGELOG.md‎

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,78 @@
55
# Unreleased
66

77

8+
<a name="v3.37.0"></a>
9+
## [v3.37.0] - 2026-10-22
10+
## Bug Fixes
11+
12+
* **event_handler:** resolve Depends() and Request params with a None default on Python 3.10 ([#8649](https://github.com/aws-powertools/powertools-lambda-python/issues/8649))
13+
14+
## Maintenance
15+
16+
* version bump
17+
18+
819
<a name="v3.36.0"></a>
920
## [v3.36.0] - 2026-10-15
1021
## Bug Fixes
1122

23+
* **data-classes:** handle keyword arguments in CaseInsensitiveDict.update ([#8518](https://github.com/aws-powertools/powertools-lambda-python/issues/8518))
24+
* **data-classes:** keep the value of large DynamoDB numbers ([#8513](https://github.com/aws-powertools/powertools-lambda-python/issues/8513))
25+
* **event_handler:** skip compression when the response has no body ([#8485](https://github.com/aws-powertools/powertools-lambda-python/issues/8485))
26+
* **event_handler:** emit Max-Age=0 for cookies with max_age=0 ([#8488](https://github.com/aws-powertools/powertools-lambda-python/issues/8488))
27+
* **feature_flags:** warn on comparator evaluation errors ([#8470](https://github.com/aws-powertools/powertools-lambda-python/issues/8470))
28+
* **idempotency:** apply jmespath_options to payload_validation_jmespath ([#8474](https://github.com/aws-powertools/powertools-lambda-python/issues/8474))
29+
* **idempotency:** serialize UUID and datetime fields in dataclass payloads ([#8501](https://github.com/aws-powertools/powertools-lambda-python/issues/8501))
1230
* **layer:** remove urllib3 from the Lambda layer again ([#8556](https://github.com/aws-powertools/powertools-lambda-python/issues/8556))
31+
* **metrics:** preserve string default dimensions across instances ([#8404](https://github.com/aws-powertools/powertools-lambda-python/issues/8404))
32+
* **streaming:** return empty bytes when reading at the end of an S3 object ([#8511](https://github.com/aws-powertools/powertools-lambda-python/issues/8511))
33+
34+
## Features
35+
36+
* **auth:** add OAuth2 client credentials support (alpha) ([#8486](https://github.com/aws-powertools/powertools-lambda-python/issues/8486))
37+
* **auth:** add JWT verification and API Gateway authorization ([#8469](https://github.com/aws-powertools/powertools-lambda-python/issues/8469))
38+
* **event_handler:** add opt-in ALB response size validation ([#8490](https://github.com/aws-powertools/powertools-lambda-python/issues/8490))
1339

1440
## Maintenance
1541

1642
* version bump
43+
* **deps:** bump datadog-lambda from 8.122.0 to 8.128.0 ([#8503](https://github.com/aws-powertools/powertools-lambda-python/issues/8503))
44+
* **deps:** bump soupsieve from 2.8.4 to 2.9 in /docs ([#8471](https://github.com/aws-powertools/powertools-lambda-python/issues/8471))
45+
* **deps:** bump the github-actions group with 8 updates ([#8549](https://github.com/aws-powertools/powertools-lambda-python/issues/8549))
46+
* **deps:** bump typing-extensions from 4.15.0 to 4.16.0 ([#8482](https://github.com/aws-powertools/powertools-lambda-python/issues/8482))
47+
* **deps:** bump anyio from 4.12.1 to 4.14.2 ([#8475](https://github.com/aws-powertools/powertools-lambda-python/issues/8475))
48+
* **deps:** bump release-drafter/release-drafter from 7.7.0 to 7.9.0 in the github-actions group ([#8521](https://github.com/aws-powertools/powertools-lambda-python/issues/8521))
49+
* **deps:** bump the github-actions group with 5 updates ([#8481](https://github.com/aws-powertools/powertools-lambda-python/issues/8481))
50+
* **deps:** bump pydantic from 2.13.4 to 2.13.5 ([#8479](https://github.com/aws-powertools/powertools-lambda-python/issues/8479))
51+
* **deps:** bump the github-actions group with 2 updates ([#8497](https://github.com/aws-powertools/powertools-lambda-python/issues/8497))
52+
* **deps:** bump pyjwt from 2.14.0 to 2.15.0 ([#8506](https://github.com/aws-powertools/powertools-lambda-python/issues/8506))
53+
* **deps:** bump urllib3 from 2.7.0 to 2.8.0 in /docs ([#8533](https://github.com/aws-powertools/powertools-lambda-python/issues/8533))
54+
* **deps:** bump pyjwt from 2.15.0 to 2.15.1 ([#8530](https://github.com/aws-powertools/powertools-lambda-python/issues/8530))
55+
* **deps:** bump gitpython from 3.1.59 to 3.1.62 ([#8531](https://github.com/aws-powertools/powertools-lambda-python/issues/8531))
56+
* **deps:** bump cryptography from 50.0.1 to 50.0.2 ([#8527](https://github.com/aws-powertools/powertools-lambda-python/issues/8527))
57+
* **deps:** bump mkdocs-llmstxt from 0.2.0 to 0.5.0 ([#8507](https://github.com/aws-powertools/powertools-lambda-python/issues/8507))
58+
* **deps:** bump pydantic-settings from 2.14.2 to 2.15.0 ([#8504](https://github.com/aws-powertools/powertools-lambda-python/issues/8504))
59+
* **deps-dev:** bump types-requests from 2.33.0.20260712 to 2.33.0.20260906 ([#8505](https://github.com/aws-powertools/powertools-lambda-python/issues/8505))
60+
* **deps-dev:** bump aws-cdk from 2.1143.0 to 2.1145.0 in the aws-cdk group ([#8547](https://github.com/aws-powertools/powertools-lambda-python/issues/8547))
61+
* **deps-dev:** bump urllib3 from 2.7.0 to 2.8.0 in /layer_v3 ([#8532](https://github.com/aws-powertools/powertools-lambda-python/issues/8532))
62+
* **deps-dev:** bump protobuf from 7.35.1 to 7.36.2 ([#8528](https://github.com/aws-powertools/powertools-lambda-python/issues/8528))
63+
* **deps-dev:** bump aws-cdk from 2.1142.0 to 2.1143.0 in the aws-cdk group ([#8491](https://github.com/aws-powertools/powertools-lambda-python/issues/8491))
64+
* **deps-dev:** bump ty from 0.0.49 to 0.0.84 ([#8498](https://github.com/aws-powertools/powertools-lambda-python/issues/8498))
65+
* **deps-dev:** bump types-protobuf from 7.34.1.20260518 to 7.35.1.20260906 ([#8495](https://github.com/aws-powertools/powertools-lambda-python/issues/8495))
66+
* **deps-dev:** bump ruff from 0.16.8 to 0.16.9 in the dev-dependencies group across 1 directory ([#8493](https://github.com/aws-powertools/powertools-lambda-python/issues/8493))
67+
* **deps-dev:** bump sentry-sdk from 2.63.0 to 2.70.0 ([#8496](https://github.com/aws-powertools/powertools-lambda-python/issues/8496))
68+
* **deps-dev:** bump filelock from 3.29.7 to 4.0.3 ([#8494](https://github.com/aws-powertools/powertools-lambda-python/issues/8494))
69+
* **deps-dev:** bump the dev-dependencies group across 1 directory with 2 updates ([#8478](https://github.com/aws-powertools/powertools-lambda-python/issues/8478))
70+
* **deps-dev:** bump aws-cdk from 2.1141.0 to 2.1142.0 in the aws-cdk group ([#8477](https://github.com/aws-powertools/powertools-lambda-python/issues/8477))
71+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.266.0a0 to 2.272.0a0 ([#8529](https://github.com/aws-powertools/powertools-lambda-python/issues/8529))
72+
* **deps-dev:** bump the dev-dependencies group across 1 directory with 3 updates ([#8526](https://github.com/aws-powertools/powertools-lambda-python/issues/8526))
73+
* **deps-dev:** bump sentry-sdk from 2.70.0 to 2.71.0 ([#8554](https://github.com/aws-powertools/powertools-lambda-python/issues/8554))
74+
* **deps-dev:** bump boto3-stubs from 1.43.92 to 1.43.97 ([#8480](https://github.com/aws-powertools/powertools-lambda-python/issues/8480))
75+
* **deps-dev:** bump ruff from 0.16.10 to 0.17.0 in the dev-dependencies group across 1 directory ([#8553](https://github.com/aws-powertools/powertools-lambda-python/issues/8553))
76+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.272.0a0 to 2.273.0a0 ([#8550](https://github.com/aws-powertools/powertools-lambda-python/issues/8550))
77+
* **deps-dev:** bump soupsieve from 2.8.4 to 2.9 ([#8472](https://github.com/aws-powertools/powertools-lambda-python/issues/8472))
78+
* **deps-dev:** bump types-requests from 2.33.0.20260906 to 2.33.0.20261006 ([#8552](https://github.com/aws-powertools/powertools-lambda-python/issues/8552))
79+
* **deps-dev:** bump nox from 2026.4.10 to 2026.8.17 ([#8551](https://github.com/aws-powertools/powertools-lambda-python/issues/8551))
1780

1881

1982
<a name="v3.35.0"></a>
@@ -7895,7 +7958,8 @@
78957958
* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38
78967959

78977960

7898-
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.36.0...HEAD
7961+
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.37.0...HEAD
7962+
[v3.37.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.36.0...v3.37.0
78997963
[v3.36.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.35.0...v3.36.0
79007964
[v3.35.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.34.0...v3.35.0
79017965
[v3.34.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.31.1...v3.34.0

‎aws_lambda_powertools/event_handler/api_gateway.py‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,8 +1482,11 @@ def _find_request_param_name(func: Callable) -> str | None:
14821482
except Exception:
14831483
hints = {}
14841484

1485+
from aws_lambda_powertools.event_handler.depends import _unwrap_optional
1486+
14851487
for param_name, annotation in hints.items():
1486-
if annotation is Request:
1488+
# Python 3.10 wraps annotations of ``= None`` parameters in Optional[...]
1489+
if _unwrap_optional(annotation) is Request:
14871490
return param_name
14881491

14891492
return None
@@ -2762,7 +2765,7 @@ def not_found_handler():
27622765
_headers["Access-Control-Allow-Methods"] = CORSConfig.build_allow_methods(self._cors_methods)
27632766
return Response(status_code=204, content_type=None, headers=_headers, body="")
27642767

2765-
custom_not_found_handler = self.exception_handler_manager.lookup_exception_handler(NotFoundError)
2768+
custom_not_found_handler = self._lookup_not_found_handler()
27662769
if custom_not_found_handler:
27672770
return custom_not_found_handler(NotFoundError())
27682771

@@ -2930,7 +2933,7 @@ def not_found_handler():
29302933
return Response(status_code=204, content_type=None, headers=_headers, body="")
29312934

29322935
# Customer registered 404 route? Call it.
2933-
custom_not_found_handler = self.exception_handler_manager.lookup_exception_handler(NotFoundError)
2936+
custom_not_found_handler = self._lookup_not_found_handler()
29342937
if custom_not_found_handler:
29352938
return custom_not_found_handler(NotFoundError())
29362939

@@ -3005,6 +3008,21 @@ def not_found(self, func: Callable | None = None):
30053008
def exception_handler(self, exc_class: type[Exception] | list[type[Exception]]):
30063009
return self.exception_handler_manager.exception_handler(exc_class=exc_class)
30073010

3011+
def _lookup_not_found_handler(self) -> Callable | None:
3012+
"""Find the handler for an unmatched route, ignoring catch-all handlers.
3013+
3014+
No exception was raised for an unmatched route, so a handler registered for `Exception` or `BaseException`
3015+
(e.g. a generic 500 handler) must not turn it into its own response. Handlers for `NotFoundError` and
3016+
`ServiceError` still apply.
3017+
"""
3018+
handlers = self.exception_handler_manager.get_registered_handlers()
3019+
for cls in NotFoundError.__mro__:
3020+
if cls in (Exception, BaseException):
3021+
break
3022+
if cls in handlers:
3023+
return handlers[cls]
3024+
return None
3025+
30083026
def _call_exception_handler(self, exp: Exception, route: Route) -> ResponseBuilder | None:
30093027
# Depends() wraps what a dependency raised in DependencyResolutionError. Handle the original exception
30103028
# as if the route had raised it, unless a handler is registered for DependencyResolutionError itself.
@@ -3019,7 +3037,12 @@ def _call_exception_handler(self, exp: Exception, route: Route) -> ResponseBuild
30193037
handler = self.exception_handler_manager.lookup_exception_handler(type(exp))
30203038
if handler:
30213039
try:
3022-
return self._response_builder_class(response=handler(exp), serializer=self._serializer, route=route)
3040+
# Adapt the handler's return value like a route's (dict/tuple, ALB null body)
3041+
return self._response_builder_class(
3042+
response=self._to_response(handler(exp)), # type: ignore[arg-type]
3043+
serializer=self._serializer,
3044+
route=route,
3045+
)
30233046
except ServiceError as service_error:
30243047
exp = service_error
30253048

@@ -3477,7 +3500,6 @@ def _build_response(self, response_builder: ResponseBuilder) -> dict[str, Any]:
34773500
if handled_response is None:
34783501
raise
34793502

3480-
handled_response.response = cast(Response, self._to_response(handled_response.response))
34813503
response = super()._build_response(handled_response)
34823504
# Validate once more without invoking an exception handler recursively.
34833505
self._validate_response_size(response)

‎aws_lambda_powertools/event_handler/depends.py‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
from __future__ import annotations
44

5-
from typing import TYPE_CHECKING, Annotated, Any, get_args, get_origin, get_type_hints
5+
from types import UnionType
6+
from typing import TYPE_CHECKING, Annotated, Any, Union, get_args, get_origin, get_type_hints
67

78
if TYPE_CHECKING:
89
from collections.abc import Callable
@@ -89,8 +90,23 @@ def __init__(self, *, param_name: str, depends: Depends, dependant: Dependant) -
8990
self.dependant = dependant
9091

9192

93+
def _unwrap_optional(annotation: Any) -> Any:
94+
"""Return the inner type of ``Optional[X]`` / ``X | None``, or the annotation itself.
95+
96+
On Python 3.10, ``typing.get_type_hints`` wraps the annotation of a parameter whose
97+
default is ``None`` in ``Optional[...]``, which would hide ``Annotated`` metadata such
98+
as ``Depends()`` or a ``Request`` annotation. Python 3.11 stopped doing that.
99+
"""
100+
if get_origin(annotation) in (Union, UnionType):
101+
args = [arg for arg in get_args(annotation) if arg is not type(None)]
102+
if len(args) == 1:
103+
return args[0]
104+
return annotation
105+
106+
92107
def _get_depends_from_annotation(annotation: Any) -> Depends | None:
93108
"""Extract a Depends instance from an Annotated[Type, Depends(...)] annotation."""
109+
annotation = _unwrap_optional(annotation)
94110
if get_origin(annotation) is Annotated:
95111
for arg in get_args(annotation)[1:]:
96112
if isinstance(arg, Depends):
@@ -202,7 +218,7 @@ def solve_dependencies(
202218
except Exception: # pragma: no cover - defensive for broken annotations
203219
hints = {}
204220
for param_name, annotation in hints.items():
205-
if annotation is RequestClass:
221+
if _unwrap_optional(annotation) is RequestClass:
206222
sub_values[param_name] = request
207223

208224
try:

0 commit comments

Comments
 (0)