Skip to content

Commit af49f0b

Browse files
Merge branch 'develop' into dependabot/npm_and_yarn/develop/aws-cdk-8aaedd9ff9
2 parents ce56f4e + fa5d1a8 commit af49f0b

File tree

40 files changed

+1230
-462
lines changed

40 files changed

+1230
-462
lines changed

.github/workflows/quality_check.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
quality_check:
4444
runs-on: ubuntu-latest
4545
strategy:
46+
fail-fast: false
4647
max-parallel: 5
4748
matrix:
4849
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

CHANGELOG.md

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

77

8+
<a name="v3.26.0"></a>
9+
## [v3.26.0] - 2026-03-20
10+
## Features
11+
12+
* add ldms feature ([#8051](https://github.com/aws-powertools/powertools-lambda-python/issues/8051))
13+
14+
## Maintenance
15+
16+
* version bump
17+
18+
819
<a name="v3.25.0"></a>
920
## [v3.25.0] - 2026-03-12
1021
## Bug Fixes
1122

1223
* **ci:** remove DUB region ([#8031](https://github.com/aws-powertools/powertools-lambda-python/issues/8031))
24+
* **event-handler:** prevent OpenAPI schema bleed when reusing response dictionaries ([#7952](https://github.com/aws-powertools/powertools-lambda-python/issues/7952))
25+
* **event_handler:** add middleware validation per route ([#8020](https://github.com/aws-powertools/powertools-lambda-python/issues/8020))
26+
* **event_handler:** fix bug regression in Annotated field ([#7904](https://github.com/aws-powertools/powertools-lambda-python/issues/7904))
27+
* **event_handler:** return 415 status_code for unsupported content-type headers ([#7980](https://github.com/aws-powertools/powertools-lambda-python/issues/7980))
28+
* **event_handler:** sync alias and validation_alias for Pydantic 2.12+ compatibility ([#7901](https://github.com/aws-powertools/powertools-lambda-python/issues/7901))
29+
* **event_handler:** preserve openapi_examples on Body ([#7862](https://github.com/aws-powertools/powertools-lambda-python/issues/7862))
30+
* **logger:** preserve percent-style formatting args in flush_buffer ([#8009](https://github.com/aws-powertools/powertools-lambda-python/issues/8009))
31+
* **parameters:** fix variable shadowing in SSM parameter chunking ([#8006](https://github.com/aws-powertools/powertools-lambda-python/issues/8006))
32+
* **typing:** resolve ty diagnostics in logging and metrics modules ([#7953](https://github.com/aws-powertools/powertools-lambda-python/issues/7953))
33+
* **typing:** accept Mapping type in resolve() for event parameter ([#7909](https://github.com/aws-powertools/powertools-lambda-python/issues/7909))
34+
35+
## Code Refactoring
36+
37+
* **batch:** improve type annotation for event parameter ([#7924](https://github.com/aws-powertools/powertools-lambda-python/issues/7924))
38+
39+
## Documentation
40+
41+
* clarify append_context_keys behavior with overlapping keys ([#7846](https://github.com/aws-powertools/powertools-lambda-python/issues/7846))
42+
43+
## Features
44+
45+
* Add a flag to ALBResolver to URL-decode query parameters ([#7940](https://github.com/aws-powertools/powertools-lambda-python/issues/7940))
46+
* add HttpResolverAlpha resolver ([#7913](https://github.com/aws-powertools/powertools-lambda-python/issues/7913))
47+
* **decorators:** Support Durable Context in logger and metric decorators ([#7765](https://github.com/aws-powertools/powertools-lambda-python/issues/7765))
48+
* **event-handler:** add per-route validation support ([#7965](https://github.com/aws-powertools/powertools-lambda-python/issues/7965))
49+
* **event_source:** add support for S3 IntelligentTiering events ([#7954](https://github.com/aws-powertools/powertools-lambda-python/issues/7954))
50+
* **metrics:** add support for multiple dimension sets ([#7848](https://github.com/aws-powertools/powertools-lambda-python/issues/7848))
51+
* **openapi:** add support for micro Lambda pattern ([#7920](https://github.com/aws-powertools/powertools-lambda-python/issues/7920))
1352

1453
## Maintenance
1554

55+
* remove unused PR automation workflows ([#8008](https://github.com/aws-powertools/powertools-lambda-python/issues/8008))
56+
* adding fuzzing tests ([#7903](https://github.com/aws-powertools/powertools-lambda-python/issues/7903))
57+
* update swagger ui files ([#7914](https://github.com/aws-powertools/powertools-lambda-python/issues/7914))
1658
* version bump
59+
* **ci:** new pre-release 3.24.1a1 ([#7926](https://github.com/aws-powertools/powertools-lambda-python/issues/7926))
60+
* **deps:** bump jmespath from 1.0.1 to 1.1.0 ([#7970](https://github.com/aws-powertools/powertools-lambda-python/issues/7970))
61+
* **deps:** bump urllib3 from 2.6.0 to 2.6.3 in /docs ([#7921](https://github.com/aws-powertools/powertools-lambda-python/issues/7921))
62+
* **deps:** bump squidfunk/mkdocs-material from `3bba0a9` to `8f41b60` in /docs ([#8010](https://github.com/aws-powertools/powertools-lambda-python/issues/8010))
63+
* **deps:** bump the github-actions group with 4 updates ([#8013](https://github.com/aws-powertools/powertools-lambda-python/issues/8013))
64+
* **deps:** bump protobuf from 6.33.2 to 6.33.4 ([#7948](https://github.com/aws-powertools/powertools-lambda-python/issues/7948))
65+
* **deps:** bump valkey-glide from 2.2.3 to 2.2.5 ([#7947](https://github.com/aws-powertools/powertools-lambda-python/issues/7947))
66+
* **deps:** bump actions/dependency-review-action from 4.8.2 to 4.8.3 in the github-actions group ([#8004](https://github.com/aws-powertools/powertools-lambda-python/issues/8004))
67+
* **deps:** bump cryptography from 46.0.3 to 46.0.5 ([#7991](https://github.com/aws-powertools/powertools-lambda-python/issues/7991))
68+
* **deps:** bump the github-actions group with 3 updates ([#7960](https://github.com/aws-powertools/powertools-lambda-python/issues/7960))
69+
* **deps:** bump protobuf from 6.33.4 to 6.33.5 ([#7977](https://github.com/aws-powertools/powertools-lambda-python/issues/7977))
70+
* **deps:** bump datadog-lambda from 8.120.0 to 8.121.0 ([#8015](https://github.com/aws-powertools/powertools-lambda-python/issues/8015))
71+
* **deps:** bump the github-actions group with 3 updates ([#7971](https://github.com/aws-powertools/powertools-lambda-python/issues/7971))
72+
* **deps:** bump the github-actions group with 2 updates ([#7985](https://github.com/aws-powertools/powertools-lambda-python/issues/7985))
73+
* **deps-dev:** bump the dev-dependencies group with 2 updates ([#7969](https://github.com/aws-powertools/powertools-lambda-python/issues/7969))
74+
* **deps-dev:** bump cdklabs-generative-ai-cdk-constructs from 0.1.312 to 0.1.314 ([#7973](https://github.com/aws-powertools/powertools-lambda-python/issues/7973))
75+
* **deps-dev:** bump cfn-lint from 1.43.3 to 1.43.4 ([#7972](https://github.com/aws-powertools/powertools-lambda-python/issues/7972))
76+
* **deps-dev:** bump aws-cdk-lib from 2.233.0 to 2.236.0 ([#7974](https://github.com/aws-powertools/powertools-lambda-python/issues/7974))
77+
* **deps-dev:** bump aws-cdk from 2.1103.0 to 2.1105.0 in the aws-cdk group ([#7982](https://github.com/aws-powertools/powertools-lambda-python/issues/7982))
78+
* **deps-dev:** bump aws-cdk from 2.1101.0 to 2.1103.0 in the aws-cdk group ([#7967](https://github.com/aws-powertools/powertools-lambda-python/issues/7967))
79+
* **deps-dev:** bump sentry-sdk from 2.48.0 to 2.52.0 ([#7987](https://github.com/aws-powertools/powertools-lambda-python/issues/7987))
80+
* **deps-dev:** bump testcontainers from 4.14.0 to 4.14.1 ([#7988](https://github.com/aws-powertools/powertools-lambda-python/issues/7988))
81+
* **deps-dev:** bump cfn-lint from 1.43.2 to 1.43.3 ([#7958](https://github.com/aws-powertools/powertools-lambda-python/issues/7958))
82+
* **deps-dev:** bump testcontainers from 4.13.3 to 4.14.0 ([#7959](https://github.com/aws-powertools/powertools-lambda-python/issues/7959))
83+
* **deps-dev:** bump multiprocess from 0.70.18 to 0.70.19 ([#7961](https://github.com/aws-powertools/powertools-lambda-python/issues/7961))
84+
* **deps-dev:** bump aws-cdk from 2.1100.3 to 2.1101.0 in the aws-cdk group ([#7955](https://github.com/aws-powertools/powertools-lambda-python/issues/7955))
85+
* **deps-dev:** bump ruff from 0.14.11 to 0.14.13 in the dev-dependencies group ([#7957](https://github.com/aws-powertools/powertools-lambda-python/issues/7957))
86+
* **deps-dev:** bump bandit from 1.9.2 to 1.9.3 ([#7962](https://github.com/aws-powertools/powertools-lambda-python/issues/7962))
87+
* **deps-dev:** bump aws-cdk-lib from 2.237.1 to 2.238.0 ([#7986](https://github.com/aws-powertools/powertools-lambda-python/issues/7986))
88+
* **deps-dev:** bump virtualenv from 20.35.4 to 20.36.1 ([#7950](https://github.com/aws-powertools/powertools-lambda-python/issues/7950))
89+
* **deps-dev:** bump aws-cdk from 2.1100.2 to 2.1100.3 in the aws-cdk group ([#7942](https://github.com/aws-powertools/powertools-lambda-python/issues/7942))
90+
* **deps-dev:** bump boto3-stubs from 1.42.21 to 1.42.26 ([#7945](https://github.com/aws-powertools/powertools-lambda-python/issues/7945))
91+
* **deps-dev:** bump aws-cdk from 2.1105.0 to 2.1106.0 in the aws-cdk group ([#7995](https://github.com/aws-powertools/powertools-lambda-python/issues/7995))
92+
* **deps-dev:** bump ruff from 0.14.10 to 0.14.11 in the dev-dependencies group ([#7944](https://github.com/aws-powertools/powertools-lambda-python/issues/7944))
93+
* **deps-dev:** bump filelock from 3.20.2 to 3.20.3 ([#7946](https://github.com/aws-powertools/powertools-lambda-python/issues/7946))
94+
* **deps-dev:** bump filelock from 3.20.3 to 3.24.2 ([#7999](https://github.com/aws-powertools/powertools-lambda-python/issues/7999))
95+
* **deps-dev:** bump sentry-sdk from 2.52.0 to 2.53.0 ([#7998](https://github.com/aws-powertools/powertools-lambda-python/issues/7998))
96+
* **deps-dev:** bump urllib3 from 2.6.2 to 2.6.3 in /layer_v3 ([#7928](https://github.com/aws-powertools/powertools-lambda-python/issues/7928))
97+
* **deps-dev:** bump types-python-dateutil from 2.9.0.20251115 to 2.9.0.20260124 ([#7989](https://github.com/aws-powertools/powertools-lambda-python/issues/7989))
98+
* **deps-dev:** bump aws-cdk-aws-lambda-python-alpha from 2.233.0a0 to 2.238.0a0 ([#7997](https://github.com/aws-powertools/powertools-lambda-python/issues/7997))
99+
* **deps-dev:** bump cfn-lint from 1.44.0 to 1.46.0 ([#8018](https://github.com/aws-powertools/powertools-lambda-python/issues/8018))
100+
* **deps-dev:** bump aws-cdk from 2.1106.0 to 2.1108.0 in the aws-cdk group ([#8011](https://github.com/aws-powertools/powertools-lambda-python/issues/8011))
101+
* **deps-dev:** bump cfn-lint from 1.43.1 to 1.43.2 ([#7907](https://github.com/aws-powertools/powertools-lambda-python/issues/7907))
102+
* **deps-dev:** bump aws-cdk from 2.1100.1 to 2.1100.2 in the aws-cdk group ([#7905](https://github.com/aws-powertools/powertools-lambda-python/issues/7905))
103+
* **deps-dev:** bump filelock from 3.24.2 to 3.25.0 ([#8016](https://github.com/aws-powertools/powertools-lambda-python/issues/8016))
104+
* **deps-dev:** bump urllib3 from 2.6.2 to 2.6.3 ([#7922](https://github.com/aws-powertools/powertools-lambda-python/issues/7922))
105+
* **typing:** add ty type checker to CI with baseline exclusions ([#7938](https://github.com/aws-powertools/powertools-lambda-python/issues/7938))
17106

18107

19108
<a name="v3.24.0"></a>
@@ -7462,7 +7551,8 @@
74627551
* Merge pull request [#5](https://github.com/aws-powertools/powertools-lambda-python/issues/5) from jfuss/feat/python38
74637552

74647553

7465-
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.25.0...HEAD
7554+
[Unreleased]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.26.0...HEAD
7555+
[v3.26.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.25.0...v3.26.0
74667556
[v3.25.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.24.0...v3.25.0
74677557
[v3.24.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.23.0...v3.24.0
74687558
[v3.23.0]: https://github.com/aws-powertools/powertools-lambda-python/compare/v3.22.1...v3.23.0

aws_lambda_powertools/shared/constants.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,12 @@
6767
PRETTY_INDENT: int = 4
6868
COMPACT_INDENT: None = None
6969

70+
# Metadata constants
71+
LAMBDA_METADATA_API_ENV: str = "AWS_LAMBDA_METADATA_API"
72+
LAMBDA_METADATA_TOKEN_ENV: str = "AWS_LAMBDA_METADATA_TOKEN"
73+
METADATA_API_VERSION: str = "2026-01-15"
74+
METADATA_PATH: str = "/metadata/execution-environment"
75+
METADATA_DEFAULT_TIMEOUT_SECS: float = 1.0
76+
7077
# Idempotency constants
7178
IDEMPOTENCY_DISABLED_ENV: str = "POWERTOOLS_IDEMPOTENCY_DISABLED"
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.25.0"
3+
VERSION = "3.26.0"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
Utility to fetch data from the AWS Lambda Metadata Endpoint
3+
"""
4+
5+
from aws_lambda_powertools.utilities.metadata.exceptions import LambdaMetadataError
6+
from aws_lambda_powertools.utilities.metadata.lambda_metadata import (
7+
LambdaMetadata,
8+
clear_metadata_cache,
9+
get_lambda_metadata,
10+
)
11+
12+
__all__ = [
13+
"LambdaMetadata",
14+
"LambdaMetadataError",
15+
"get_lambda_metadata",
16+
"clear_metadata_cache",
17+
]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""
2+
Lambda Metadata Service exceptions
3+
"""
4+
5+
6+
class LambdaMetadataError(Exception):
7+
"""Raised when the Lambda Metadata Endpoint is unavailable or returns an error."""
8+
9+
def __init__(self, message: str, status_code: int = -1):
10+
self.status_code = status_code
11+
super().__init__(message)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"""
2+
Lambda Metadata Service client
3+
4+
Fetches execution environment metadata from the Lambda Metadata Endpoint,
5+
with caching for the sandbox lifetime.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import logging
11+
import os
12+
import urllib.request
13+
from dataclasses import dataclass, field
14+
from json import JSONDecodeError
15+
from json import loads as json_loads
16+
from typing import Any
17+
18+
from aws_lambda_powertools.shared.constants import (
19+
LAMBDA_INITIALIZATION_TYPE,
20+
LAMBDA_METADATA_API_ENV,
21+
LAMBDA_METADATA_TOKEN_ENV,
22+
METADATA_API_VERSION,
23+
METADATA_DEFAULT_TIMEOUT_SECS,
24+
METADATA_PATH,
25+
POWERTOOLS_DEV_ENV,
26+
)
27+
from aws_lambda_powertools.utilities.metadata.exceptions import LambdaMetadataError
28+
29+
logger = logging.getLogger(__name__)
30+
31+
_cache: dict[str, Any] = {}
32+
33+
34+
@dataclass(frozen=True)
35+
class LambdaMetadata:
36+
"""Lambda execution environment metadata returned by the metadata endpoint."""
37+
38+
availability_zone_id: str | None = None
39+
"""The Availability Zone ID where the function is executing (e.g. ``use1-az1``)."""
40+
41+
_raw: dict[str, Any] = field(default_factory=dict, repr=False)
42+
"""Full raw response for forward-compatibility with future fields."""
43+
44+
45+
def _is_lambda_environment() -> bool:
46+
"""Check whether we are running inside a Lambda execution environment."""
47+
return os.environ.get(LAMBDA_INITIALIZATION_TYPE, "") != ""
48+
49+
50+
def _is_dev_mode() -> bool:
51+
"""Check whether POWERTOOLS_DEV is enabled."""
52+
return os.environ.get(POWERTOOLS_DEV_ENV, "false").strip().lower() in ("true", "1")
53+
54+
55+
def _build_metadata(data: dict[str, Any]) -> LambdaMetadata:
56+
"""Build a LambdaMetadata dataclass from the raw endpoint response."""
57+
return LambdaMetadata(
58+
availability_zone_id=data.get("AvailabilityZoneID"),
59+
_raw=data,
60+
)
61+
62+
63+
def _fetch_metadata(timeout: float = METADATA_DEFAULT_TIMEOUT_SECS) -> dict[str, Any]:
64+
"""
65+
Fetch metadata from the Lambda Metadata Endpoint via HTTP.
66+
67+
Parameters
68+
----------
69+
timeout : float
70+
Request timeout in seconds.
71+
72+
Returns
73+
-------
74+
dict[str, Any]
75+
Parsed JSON response from the metadata endpoint.
76+
77+
Raises
78+
------
79+
LambdaMetadataError
80+
If required environment variables are missing, the endpoint returns
81+
a non-200 status, or the response cannot be parsed.
82+
"""
83+
api = os.environ.get(LAMBDA_METADATA_API_ENV)
84+
token = os.environ.get(LAMBDA_METADATA_TOKEN_ENV)
85+
86+
if not api:
87+
raise LambdaMetadataError(
88+
f"Environment variable {LAMBDA_METADATA_API_ENV} is not set. Ensure {LAMBDA_METADATA_API_ENV} is set.",
89+
)
90+
if not token:
91+
raise LambdaMetadataError(
92+
f"Environment variable {LAMBDA_METADATA_TOKEN_ENV} is not set. Ensure {LAMBDA_METADATA_TOKEN_ENV} is set.",
93+
)
94+
95+
url = f"http://{api}/{METADATA_API_VERSION}{METADATA_PATH}"
96+
logger.debug("Fetching Lambda metadata from: %s", url)
97+
98+
req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
99+
100+
try:
101+
with urllib.request.urlopen(req, timeout=timeout) as resp: # nosec B310
102+
status = resp.status
103+
body = resp.read().decode("utf-8")
104+
except urllib.error.HTTPError as exc:
105+
raise LambdaMetadataError(
106+
f"Metadata request failed with status {exc.code}",
107+
status_code=exc.code,
108+
) from exc
109+
except Exception as exc:
110+
raise LambdaMetadataError(f"Failed to fetch Lambda metadata: {exc}") from exc
111+
112+
if status != 200:
113+
raise LambdaMetadataError(
114+
f"Metadata request failed with status {status}",
115+
status_code=status,
116+
)
117+
118+
try:
119+
data: dict[str, Any] = json_loads(body)
120+
except (JSONDecodeError, TypeError) as exc:
121+
raise LambdaMetadataError(f"Failed to parse metadata response: {exc}") from exc
122+
123+
logger.debug("Lambda metadata response: %s", data)
124+
return data
125+
126+
127+
def get_lambda_metadata(*, timeout: float = METADATA_DEFAULT_TIMEOUT_SECS) -> LambdaMetadata:
128+
"""
129+
Retrieve Lambda execution environment metadata.
130+
131+
Returns cached metadata on subsequent calls. When not running in a Lambda
132+
environment (local dev, tests) or when ``POWERTOOLS_DEV`` is enabled,
133+
returns an empty ``LambdaMetadata``.
134+
135+
Parameters
136+
----------
137+
timeout : float
138+
HTTP request timeout in seconds (default 1.0).
139+
140+
Returns
141+
-------
142+
LambdaMetadata
143+
Metadata about the current execution environment.
144+
145+
Raises
146+
------
147+
LambdaMetadataError
148+
If the metadata endpoint is unavailable or returns an error.
149+
150+
Example
151+
-------
152+
>>> from aws_lambda_powertools.utilities.metadata import get_lambda_metadata
153+
>>> metadata = get_lambda_metadata()
154+
>>> metadata.availability_zone_id # e.g. "use1-az1"
155+
"""
156+
if _is_dev_mode() or not _is_lambda_environment():
157+
return LambdaMetadata()
158+
159+
if _cache:
160+
return _build_metadata(_cache)
161+
162+
data = _fetch_metadata(timeout=timeout)
163+
_cache.update(data)
164+
return _build_metadata(_cache)
165+
166+
167+
def clear_metadata_cache() -> None:
168+
"""
169+
Clear the cached metadata.
170+
171+
Useful for testing or when you need to force a fresh fetch
172+
(e.g. after SnapStart restore).
173+
"""
174+
_cache.clear()

0 commit comments

Comments
 (0)