Skip to content

Commit a0ef1f7

Browse files
Merge branch 'develop' into refactor/8097-extract-openapi-schema-generator
2 parents b853c9f + 8e816fb commit a0ef1f7

44 files changed

Lines changed: 2043 additions & 448 deletions

Some content is hidden

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

.github/workflows/reusable_deploy_v3_layer_stack.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
"ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3",
7777
"ap-southeast-4", "ap-southeast-5", "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", "eu-central-2",
7878
"eu-north-1", "eu-south-1", "eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3",
79-
"il-central-1", "me-south-1", "mx-central-1", "sa-east-1", "us-east-1",
79+
"il-central-1", "mx-central-1", "sa-east-1", "us-east-1",
8080
"us-east-2", "us-west-1", "us-west-2"]
8181
python-version: ["3.10","3.11","3.12","3.13","3.14"]
8282
include:
@@ -128,8 +128,6 @@ jobs:
128128
has_arm64_support: "true"
129129
- region: "il-central-1"
130130
has_arm64_support: "true"
131-
- region: "me-south-1"
132-
has_arm64_support: "true"
133131
- region: "mx-central-1"
134132
has_arm64_support: "true"
135133
- region: "sa-east-1"

.github/workflows/update_ssm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
"ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-3",
7979
"ap-southeast-4", "ap-southeast-5", "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", "eu-central-2",
8080
"eu-north-1", "eu-south-1", "eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3",
81-
"il-central-1", "me-south-1", "mx-central-1", "sa-east-1", "us-east-1",
81+
"il-central-1", "mx-central-1", "sa-east-1", "us-east-1",
8282
"us-east-2", "us-west-1", "us-west-2"]
8383

8484
permissions:

CHANGELOG.md

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

77

8+
<a name="v3.27.0"></a>
9+
## [v3.27.0] - 2026-04-06
10+
## Maintenance
11+
12+
* version bump
13+
14+
815
<a name="v3.26.0"></a>
916
## [v3.26.0] - 2026-03-20
17+
## Bug Fixes
18+
19+
* **ci:** add ty check to dataclasses utility ([#8038](https://github.com/aws-powertools/powertools-lambda-python/issues/8038))
20+
* **ci:** add ty check to parser folder ([#8037](https://github.com/aws-powertools/powertools-lambda-python/issues/8037))
21+
* **ci:** add ty check to parameters folder ([#8035](https://github.com/aws-powertools/powertools-lambda-python/issues/8035))
22+
* **openapi:** correct response validation for falsy objects ([#7990](https://github.com/aws-powertools/powertools-lambda-python/issues/7990))
23+
1024
## Features
1125

1226
* add ldms feature ([#8051](https://github.com/aws-powertools/powertools-lambda-python/issues/8051))
27+
* **batch:** add Kafka/MSK batch processing support ([#7941](https://github.com/aws-powertools/powertools-lambda-python/issues/7941))
28+
* **buffer-handler:** add buffering support for external loggers ([#7994](https://github.com/aws-powertools/powertools-lambda-python/issues/7994))
1329

1430
## Maintenance
1531

1632
* version bump
33+
* **deps:** bump aws-encryption-sdk from 4.0.3 to 4.0.4 ([#8027](https://github.com/aws-powertools/powertools-lambda-python/issues/8027))
34+
* **deps:** bump valkey-glide from 2.2.5 to 2.2.7 ([#8030](https://github.com/aws-powertools/powertools-lambda-python/issues/8030))
35+
* **deps-dev:** bump types-python-dateutil from 2.9.0.20260124 to 2.9.0.20260305 ([#8029](https://github.com/aws-powertools/powertools-lambda-python/issues/8029))
36+
* **deps-dev:** bump ijson from 3.4.0.post0 to 3.5.0 ([#8028](https://github.com/aws-powertools/powertools-lambda-python/issues/8028))
37+
* **deps-dev:** bump aws-cdk from 2.1108.0 to 2.1110.0 in the aws-cdk group ([#8023](https://github.com/aws-powertools/powertools-lambda-python/issues/8023))
1738

1839

1940
<a name="v3.25.0"></a>
@@ -7551,7 +7572,8 @@
75517572
* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38
75527573

75537574

7554-
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.26.0...HEAD
7575+
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.27.0...HEAD
7576+
[v3.27.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.26.0...v3.27.0
75557577
[v3.26.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.25.0...v3.26.0
75567578
[v3.25.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.24.0...v3.25.0
75577579
[v3.24.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.23.0...v3.24.0

aws_lambda_powertools/event_handler/http_resolver.py

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
import base64
55
import inspect
6+
import threading
67
import warnings
78
from typing import TYPE_CHECKING, Any, Callable
89
from urllib.parse import parse_qs
@@ -324,36 +325,65 @@ async def final_handler(app):
324325
return await next_handler(self)
325326

326327
def _wrap_middleware_async(self, middleware: Callable, next_handler: Callable) -> Callable:
327-
"""Wrap a middleware to work in async context."""
328+
"""Wrap a middleware to work in async context.
329+
330+
For sync middlewares, we split execution into pre/post phases around the
331+
call to next(). The sync middleware runs its pre-processing (e.g. request
332+
validation), then we intercept the next() call, await the async handler,
333+
and resume the middleware with the real response so post-processing
334+
(e.g. response validation) sees the actual data.
335+
"""
328336

329337
async def wrapped(app):
330-
# Create a next_middleware that the sync middleware can call
331-
def sync_next(app):
332-
# This will be called by sync middleware
333-
# We need to run the async next_handler
334-
loop = asyncio.get_event_loop()
335-
if loop.is_running():
336-
# We're in an async context, create a task
337-
future = asyncio.ensure_future(next_handler(app))
338-
# Store for later await
339-
app.context["_async_next_result"] = future
340-
return Response(status_code=200, body="") # Placeholder
341-
else: # pragma: no cover
342-
return loop.run_until_complete(next_handler(app))
343-
344-
# Check if middleware is async
345338
if inspect.iscoroutinefunction(middleware):
346-
result = await middleware(app, next_handler)
347-
else:
348-
# Sync middleware - need special handling
349-
result = middleware(app, sync_next)
339+
return await middleware(app, next_handler)
350340

351-
# Check if we stored an async result
352-
if "_async_next_result" in app.context:
353-
future = app.context.pop("_async_next_result")
354-
result = await future
341+
# We use an Event to coordinate: the sync middleware runs in a thread,
342+
# calls sync_next which signals us to resolve the async handler,
343+
# then waits for the real response.
344+
middleware_called_next = asyncio.Event()
345+
next_app_holder: list = []
346+
real_response_holder: list = []
347+
middleware_result_holder: list = []
348+
middleware_error_holder: list = []
355349

356-
return result
350+
def sync_next(app):
351+
next_app_holder.append(app)
352+
middleware_called_next.set()
353+
# Block this thread until the real response is available
354+
event = threading.Event()
355+
next_app_holder.append(event)
356+
event.wait()
357+
return real_response_holder[0]
358+
359+
def run_middleware():
360+
try:
361+
result = middleware(app, sync_next)
362+
middleware_result_holder.append(result)
363+
except Exception as e:
364+
middleware_error_holder.append(e)
365+
366+
thread = threading.Thread(target=run_middleware, daemon=True)
367+
thread.start()
368+
369+
# Wait for the middleware to call next()
370+
await middleware_called_next.wait()
371+
372+
# Now resolve the async next_handler
373+
real_response = await next_handler(next_app_holder[0])
374+
real_response_holder.append(real_response)
375+
376+
# Signal the thread that the response is ready
377+
threading_event = next_app_holder[1]
378+
threading_event.set()
379+
380+
# Wait for the middleware thread to finish
381+
thread.join()
382+
383+
if middleware_error_holder:
384+
raise middleware_error_holder[0]
385+
386+
return middleware_result_holder[0]
357387

358388
return wrapped
359389

0 commit comments

Comments
 (0)