Skip to content

Commit b28028b

Browse files
Merge branch 'develop' into fix/event-bridge-event-model
2 parents 805017f + f8c7faf commit b28028b

27 files changed

Lines changed: 499 additions & 365 deletions

File tree

CHANGELOG.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,72 @@
55
# Unreleased
66

77

8+
<a name="v3.31.0"></a>
9+
## [v3.31.0] - 2026-06-29
10+
## Features
11+
12+
* **event_handler:** support any Pydantic Field annotation in parameter ([#8305](https://github.com/aws-powertools/powertools-lambda-python/issues/8305))
13+
14+
## Maintenance
15+
16+
* version bump
17+
18+
819
<a name="v3.30.0"></a>
920
## [v3.30.0] - 2026-06-24
21+
## Bug Fixes
22+
23+
* **batch:** add optional logger injection for BatchProcessors ([#7553](https://github.com/aws-powertools/powertools-lambda-python/issues/7553)) ([#8272](https://github.com/aws-powertools/powertools-lambda-python/issues/8272))
24+
* **docs:** update broken AWS Lambda Metadata Endpoint link ([#8209](https://github.com/aws-powertools/powertools-lambda-python/issues/8209))
25+
* **event-handler:** handle CORS preflight OPTIONS in HttpResolverLocal ([#8268](https://github.com/aws-powertools/powertools-lambda-python/issues/8268))
26+
* **event-handler:** fix ruff lint violations in event_handler module ([#8208](https://github.com/aws-powertools/powertools-lambda-python/issues/8208))
27+
28+
## Code Refactoring
29+
30+
* **event_handler:** promote HttpResolver to stable ([#8289](https://github.com/aws-powertools/powertools-lambda-python/issues/8289))
31+
32+
## Documentation
33+
34+
* **event-handler:** add import path gotcha for dependency_overrides testing ([#8269](https://github.com/aws-powertools/powertools-lambda-python/issues/8269))
35+
* **metadata:** fix broken Lambda Metadata Endpoint link ([#8212](https://github.com/aws-powertools/powertools-lambda-python/issues/8212))
36+
37+
## Features
38+
39+
* **event_handler:** allow custom serializer on BedrockAgentResolver ([#8271](https://github.com/aws-powertools/powertools-lambda-python/issues/8271))
40+
1041
## Maintenance
1142

1243
* version bump
44+
* fix minor typos and grammar in docs and comments ([#8245](https://github.com/aws-powertools/powertools-lambda-python/issues/8245))
45+
* **deps:** bump the github-actions group across 1 directory with 5 updates ([#8256](https://github.com/aws-powertools/powertools-lambda-python/issues/8256))
46+
* **deps:** bump ujson from 5.12.1 to 5.13.0 ([#8298](https://github.com/aws-powertools/powertools-lambda-python/issues/8298))
47+
* **deps:** bump pydantic-settings from 2.14.1 to 2.14.2 ([#8299](https://github.com/aws-powertools/powertools-lambda-python/issues/8299))
48+
* **deps:** bump cryptography from 46.0.7 to 48.0.1 ([#8286](https://github.com/aws-powertools/powertools-lambda-python/issues/8286))
49+
* **deps:** consolidate Dependabot dependency updates ([#8285](https://github.com/aws-powertools/powertools-lambda-python/issues/8285))
50+
* **deps:** batch dependency updates ([#8207](https://github.com/aws-powertools/powertools-lambda-python/issues/8207))
51+
* **deps:** bump gitpython from 3.1.47 to 3.1.50 in /docs ([#8213](https://github.com/aws-powertools/powertools-lambda-python/issues/8213))
52+
* **deps:** bump urllib3 from 2.6.3 to 2.7.0 in /docs ([#8216](https://github.com/aws-powertools/powertools-lambda-python/issues/8216))
53+
* **deps:** consolidate dependabot updates (13052026) ([#8228](https://github.com/aws-powertools/powertools-lambda-python/issues/8228))
54+
* **deps:** bump the github-actions group with 3 updates ([#8221](https://github.com/aws-powertools/powertools-lambda-python/issues/8221))
55+
* **deps:** bump codecov/codecov-action from 6.0.1 to 7.0.0 in the github-actions group ([#8260](https://github.com/aws-powertools/powertools-lambda-python/issues/8260))
56+
* **deps:** consolidate all open Dependabot updates ([#8241](https://github.com/aws-powertools/powertools-lambda-python/issues/8241))
57+
* **deps:** bump the github-actions group with 2 updates ([#8292](https://github.com/aws-powertools/powertools-lambda-python/issues/8292))
58+
* **deps:** bump pydantic from 2.12.5 to 2.13.4 ([#8252](https://github.com/aws-powertools/powertools-lambda-python/issues/8252))
59+
* **deps:** update requests requirement from >=2.33.1 to >=2.34.2 in /examples/event_handler_graphql/src ([#8254](https://github.com/aws-powertools/powertools-lambda-python/issues/8254))
60+
* **deps-dev:** bump aws-cdk from 2.1127.0 to 2.1128.1 in the aws-cdk group ([#8290](https://github.com/aws-powertools/powertools-lambda-python/issues/8290))
61+
* **deps-dev:** bump the dev-dependencies group across 1 directory with 2 updates ([#8242](https://github.com/aws-powertools/powertools-lambda-python/issues/8242))
62+
* **deps-dev:** bump aws-cdk from 2.1122.0 to 2.1124.1 in the aws-cdk group across 1 directory ([#8234](https://github.com/aws-powertools/powertools-lambda-python/issues/8234))
63+
* **deps-dev:** bump boto3-stubs from 1.43.3 to 1.43.24 ([#8265](https://github.com/aws-powertools/powertools-lambda-python/issues/8265))
64+
* **deps-dev:** bump aws-cdk from 2.1124.1 to 2.1126.0 in the aws-cdk group across 1 directory ([#8255](https://github.com/aws-powertools/powertools-lambda-python/issues/8255))
65+
* **deps-dev:** bump the dev-dependencies group with 3 updates ([#8261](https://github.com/aws-powertools/powertools-lambda-python/issues/8261))
66+
* **deps-dev:** bump urllib3 from 2.6.3 to 2.7.0 in /layer_v3 ([#8218](https://github.com/aws-powertools/powertools-lambda-python/issues/8218))
67+
* **deps-dev:** bump urllib3 from 2.6.3 to 2.7.0 ([#8217](https://github.com/aws-powertools/powertools-lambda-python/issues/8217))
68+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.251.0a0 to 2.259.0a0 ([#8262](https://github.com/aws-powertools/powertools-lambda-python/issues/8262))
69+
* **deps-dev:** bump sentry-sdk from 2.57.0 to 2.62.0 ([#8263](https://github.com/aws-powertools/powertools-lambda-python/issues/8263))
70+
* **deps-dev:** bump gitpython from 3.1.47 to 3.1.50 ([#8214](https://github.com/aws-powertools/powertools-lambda-python/issues/8214))
71+
* **deps-dev:** bump the dev-dependencies group across 1 directory with 2 updates ([#8276](https://github.com/aws-powertools/powertools-lambda-python/issues/8276))
1372
* **deps-dev:** bump aws-cdk-lib from 2.259.0 to 2.260.0 ([#8294](https://github.com/aws-powertools/powertools-lambda-python/issues/8294))
73+
* **deps-dev:** bump the dev-dependencies group across 1 directory with 3 updates ([#8293](https://github.com/aws-powertools/powertools-lambda-python/issues/8293))
1474

1575

1676
<a name="v3.29.0"></a>
@@ -7699,7 +7759,8 @@
76997759
* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38
77007760

77017761

7702-
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.30.0...HEAD
7762+
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.31.0...HEAD
7763+
[v3.31.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.30.0...v3.31.0
77037764
[v3.30.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.29.0...v3.30.0
77047765
[v3.29.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.28.0...v3.29.0
77057766
[v3.28.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.27.0...v3.28.0

aws_lambda_powertools/event_handler/openapi/params.py

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -953,54 +953,52 @@ def get_field_info_response_type(annotation, value) -> tuple[FieldInfo | None, A
953953
return get_field_info_and_type_annotation(inner_type, value, False, True)
954954

955955

956-
def _has_discriminator(field_info: FieldInfo) -> bool:
957-
"""Check if a FieldInfo has a discriminator."""
958-
return hasattr(field_info, "discriminator") and field_info.discriminator is not None
959-
960-
961-
def _handle_discriminator_with_param(
956+
def _split_location_marker_and_field(
962957
annotations: list[FieldInfo],
963-
annotation: Any,
964-
) -> tuple[FieldInfo | None, Any, bool]:
958+
) -> tuple[FieldInfo, FieldInfo]:
965959
"""
966-
Handle the special case of Field(discriminator) + Body() combination.
960+
Split a pair of FieldInfo into the Powertools location marker and the plain Pydantic Field.
961+
962+
A parameter like ``Annotated[str, Field(gt=0), Query()]`` flattens to two FieldInfo: the
963+
location marker (``Path``/``Query``/``Header``/``Body``, a ``Param`` or ``Body`` subclass) and
964+
a plain Pydantic ``Field`` carrying constraints. We keep them apart so the location marker
965+
drives where the value comes from, while the ``Field``'s constraints/metadata still apply.
967966
968967
Returns:
969-
tuple of (powertools_annotation, type_annotation, has_discriminator_with_body)
968+
tuple of (location_marker, plain_field)
970969
"""
971-
field_obj = None
972-
body_obj = None
970+
location_marker: FieldInfo | None = None
971+
plain_field: FieldInfo | None = None
973972

974973
for ann in annotations:
975-
if isinstance(ann, Body):
976-
body_obj = ann
977-
elif _has_discriminator(ann):
978-
field_obj = ann
974+
if isinstance(ann, (Param, Body)):
975+
location_marker = ann
976+
else:
977+
plain_field = ann
979978

980-
if field_obj and body_obj:
981-
# Use Body as the primary annotation, preserve full annotation for validation
982-
return body_obj, annotation, True
979+
if location_marker is None or plain_field is None:
980+
raise AssertionError("Only one FieldInfo can be used per parameter")
983981

984-
raise AssertionError("Only one FieldInfo can be used per parameter")
982+
return location_marker, plain_field
985983

986984

987985
def _create_field_info(
988986
powertools_annotation: FieldInfo,
989987
type_annotation: Any,
990-
has_discriminator_with_body: bool,
988+
preserve_full_annotation: bool,
991989
) -> FieldInfo:
992990
"""Create or copy FieldInfo based on the annotation type."""
993-
field_info: FieldInfo
994-
if has_discriminator_with_body:
995-
# For discriminator + Body case, create a new Body instance directly
996-
field_info = Body()
991+
# Copy field_info because we mutate field_info.default later.
992+
field_info = copy_field_info(
993+
field_info=powertools_annotation,
994+
annotation=type_annotation,
995+
)
996+
if preserve_full_annotation:
997+
# The location marker is paired with a plain Field (constraints or discriminator).
998+
# copy_field_info strips the Pydantic Field out of the metadata, so re-attach the full
999+
# Annotated type here, that's what makes the Field's metadata flow into the TypeAdapter
1000+
# that ModelField builds for validation.
9971001
field_info.annotation = type_annotation
998-
else:
999-
# Copy field_info because we mutate field_info.default later
1000-
field_info = copy_field_info(
1001-
field_info=powertools_annotation,
1002-
annotation=type_annotation,
1003-
)
10041002
return field_info
10051003

10061004

@@ -1043,32 +1041,36 @@ def get_field_info_annotated_type(annotation, value, is_path_param: bool) -> tup
10431041

10441042
# Determine which annotation to use
10451043
powertools_annotation: FieldInfo | None = None
1046-
has_discriminator_with_param = False
1044+
# When a plain Pydantic Field is paired with a location marker (e.g. Field(gt=0) + Query),
1045+
# we keep the full Annotated type as the annotation so the Field's metadata still validates.
1046+
preserve_full_annotation = False
10471047

10481048
if len(powertools_annotations) == 2:
1049-
powertools_annotation, type_annotation, has_discriminator_with_param = _handle_discriminator_with_param(
1050-
powertools_annotations,
1051-
annotation,
1052-
)
1049+
# A location marker (Path/Query/Header/Body) plus a plain Pydantic Field carrying
1050+
# constraints, a discriminator, or any other Field setting. The marker says where the
1051+
# value comes from; the Field says how to validate it.
1052+
powertools_annotation, _ = _split_location_marker_and_field(powertools_annotations)
1053+
preserve_full_annotation = True
10531054
elif len(powertools_annotations) > 1:
10541055
raise AssertionError("Only one FieldInfo can be used per parameter")
10551056
else:
10561057
powertools_annotation = next(iter(powertools_annotations), None)
10571058

10581059
# Reconstruct type_annotation with non-FieldInfo metadata if present
10591060
# This ensures constraints like Interval are preserved
1060-
if other_metadata and not has_discriminator_with_param:
1061+
if other_metadata and not preserve_full_annotation:
10611062
type_annotation = Annotated[(type_annotation, *other_metadata)]
10621063

10631064
# Process the annotation if it exists
10641065
field_info: FieldInfo | None = None
1065-
if isinstance(powertools_annotation, FieldInfo): # pragma: no cover
1066-
field_info = _create_field_info(powertools_annotation, type_annotation, has_discriminator_with_param)
1066+
if isinstance(powertools_annotation, FieldInfo):
1067+
# When pairing a location marker with a plain Field, hand the full Annotated[...] to the
1068+
# field so the Field's constraints/discriminator flow into the validating TypeAdapter.
1069+
field_annotation = annotation if preserve_full_annotation else type_annotation
1070+
field_info = _create_field_info(powertools_annotation, field_annotation, preserve_full_annotation)
10671071
_set_field_default(field_info, value, is_path_param)
1068-
1069-
# Preserve full annotated type for discriminated unions
1070-
if _has_discriminator(powertools_annotation): # pragma: no cover
1071-
type_annotation = annotation # pragma: no cover
1072+
if preserve_full_annotation:
1073+
type_annotation = annotation
10721074

10731075
return field_info, type_annotation
10741076

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Exposes version constant to avoid circular dependencies."""
22

3-
VERSION = "3.30.0"
3+
VERSION = "3.31.0"

0 commit comments

Comments
 (0)