aws-lambda-powertools 3.25.0__tar.gz → 3.27.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/PKG-INFO +2 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/__init__.py +2 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/api_gateway.py +76 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/http_resolver.py +55 -25
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py +250 -43
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/dependant.py +10 -4
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/merge.py +11 -4
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/params.py +124 -1
- aws_lambda_powertools-3.27.0/aws_lambda_powertools/event_handler/request.py +115 -0
- aws_lambda_powertools-3.27.0/aws_lambda_powertools/logging/buffer/handler.py +72 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/utils.py +24 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/constants.py +7 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/version.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/base.py +23 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/decorators.py +43 -12
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/types.py +22 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/api_gateway_authorizer_event.py +9 -9
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/api_gateway_proxy_event.py +11 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py +3 -3
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/appsync_resolver_event.py +2 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/common.py +47 -6
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/base.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/feature_flags.py +9 -9
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/base.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/base.py +4 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/protobuf.py +3 -1
- aws_lambda_powertools-3.27.0/aws_lambda_powertools/utilities/metadata/__init__.py +17 -0
- aws_lambda_powertools-3.27.0/aws_lambda_powertools/utilities/metadata/exceptions.py +11 -0
- aws_lambda_powertools-3.27.0/aws_lambda_powertools/utilities/metadata/lambda_metadata.py +175 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/appconfig.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/base.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/dynamodb.py +2 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/secrets.py +3 -3
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/ssm.py +8 -6
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/functions.py +2 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/parser.py +1 -1
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/pyproject.toml +10 -10
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/setup.py +3 -2
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/LICENSE +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/README.md +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/THIRD-PARTY-LICENSES +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/appsync.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/bedrock_agent.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/bedrock_agent_function.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/content_types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/_registry.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/router.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/events_appsync/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/exception_handling.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/graphql_appsync/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/graphql_appsync/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/graphql_appsync/router.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/lambda_function_url.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/middlewares/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/middlewares/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/middlewares/schema_validation.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/compat.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/config.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/encoders.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/models.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui.min.css +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/openapi/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/router.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/util.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/event_handler/vpc_lattice.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/exceptions/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/buffer/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/buffer/cache.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/buffer/config.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/buffer/functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/correlation_paths.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/filters.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/formatter.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/formatters/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/formatters/datadog.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/lambda_context.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/logger.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/logging/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/metric.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/metrics.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/metric_properties.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/cold_start.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/datadog/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/datadog/datadog.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/datadog/metrics.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/provider/datadog/warnings.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/metrics/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/middleware_factory/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/middleware_factory/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/middleware_factory/factory.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/package_logger.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/py.typed +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/cache_dict.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/cookies.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/dynamodb_deserializer.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/headers_serializer.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/json_encoder.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/lazy_import.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/shared/user_agent.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/tracing/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/tracing/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/tracing/extensions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/tracing/tracer.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/active_mq_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/alb_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/api_gateway_websocket_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/appsync/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/appsync_authorizer_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/appsync_resolver_events_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/aws_config_rule_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/bedrock_agent_function_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/cloud_watch_custom_widget_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/cloud_watch_logs_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/cloudformation_custom_resource_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/code_deploy_lifecycle_hook_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/cognito_user_pool_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/connect_contact_flow_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/dynamo_db_stream_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/event_bridge_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/event_source.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/iot_registry_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/kafka_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/kinesis_firehose_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/kinesis_stream_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/lambda_function_url_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/rabbit_mq_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/s3_batch_operation_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/s3_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/s3_object_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/secrets_manager_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/ses_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/shared_functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/sns_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/sqs_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/transfer_family_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_classes/vpc_lattice.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/provider/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/provider/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/provider/kms/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/data_masking/provider/kms/aws_encryption_sdk.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/appconfig.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/comparators.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/schema.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/feature_flags/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/config.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/hook.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/idempotency.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/cache.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/datarecord.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/persistence/redis.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/custom_dict.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/dataclass.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/functions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/no_op.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/idempotency/serialization/pydantic.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/jmespath_utils/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/jmespath_utils/envelopes.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/consumer_records.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/avro.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/default.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/deserializer.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/deserializer/json.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/kafka_consumer.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/schema_config.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/custom_dict.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/dataclass.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/pydantic.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/serialization.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/kafka/serialization/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parameters/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/apigw.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/apigw_websocket.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/apigwv2.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/bedrock_agent.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/cloudwatch.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/dynamodb.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/event_bridge.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/kafka.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/kinesis.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/kinesis_firehose.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/lambda_function_url.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/sns.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/sqs.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/vpc_lattice.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/envelopes/vpc_latticev2.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/alb.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/apigw.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/apigw_websocket.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/apigwv2.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/appsync.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/appsync_events.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/bedrock_agent.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/cloudformation_custom_resource.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/cloudwatch.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/cognito.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/dynamodb.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/event_bridge.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/iot_registry_events.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/kafka.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/kinesis.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/lambda_function_url.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/s3.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/s3_batch_operation.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/s3_object_event.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/ses.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/sns.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/sqs.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/transfer_family.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/vpc_lattice.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/models/vpc_latticev2.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/parser/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/serialization.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/_s3_seekable_io.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/compat.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/constants.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/s3_object.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/transformations/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/transformations/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/transformations/csv.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/transformations/gzip.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/transformations/zip.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/streaming/types.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/typing/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/typing/lambda_client_context.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/typing/lambda_client_context_mobile_client.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/typing/lambda_cognito_identity.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/typing/lambda_context.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/validation/__init__.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/validation/base.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/validation/envelopes.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/validation/exceptions.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/utilities/validation/validator.py +0 -0
- {aws_lambda_powertools-3.25.0 → aws_lambda_powertools-3.27.0}/aws_lambda_powertools/warnings/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: aws_lambda_powertools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.27.0
|
|
4
4
|
Summary: Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless best practices and increase developer velocity.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: aws_lambda_powertools,aws,tracing,logging,lambda,powertools,feature_flags,idempotency,middleware
|
|
@@ -36,7 +36,7 @@ Requires-Dist: datadog-lambda (>=8.114.0,<9.0.0) ; extra == "datadog"
|
|
|
36
36
|
Requires-Dist: fastjsonschema (>=2.14.5,<3.0.0) ; extra == "validation" or extra == "all"
|
|
37
37
|
Requires-Dist: jmespath (>=1.0.1,<2.0.0)
|
|
38
38
|
Requires-Dist: jsonpath-ng (>=1.6.0,<2.0.0) ; extra == "all" or extra == "datamasking"
|
|
39
|
-
Requires-Dist: protobuf (>=6.30.2,<
|
|
39
|
+
Requires-Dist: protobuf (>=6.30.2,<8.0.0) ; extra == "kafka-consumer-protobuf"
|
|
40
40
|
Requires-Dist: pydantic (>=2.4.0,<3.0.0) ; extra == "parser" or extra == "all"
|
|
41
41
|
Requires-Dist: pydantic-settings (>=2.6.1,<3.0.0) ; extra == "all"
|
|
42
42
|
Requires-Dist: redis (>=4.4,<8.0) ; extra == "redis"
|
|
@@ -21,6 +21,7 @@ from aws_lambda_powertools.event_handler.http_resolver import HttpResolverLocal
|
|
|
21
21
|
from aws_lambda_powertools.event_handler.lambda_function_url import (
|
|
22
22
|
LambdaFunctionUrlResolver,
|
|
23
23
|
)
|
|
24
|
+
from aws_lambda_powertools.event_handler.request import Request
|
|
24
25
|
from aws_lambda_powertools.event_handler.vpc_lattice import VPCLatticeResolver, VPCLatticeV2Resolver
|
|
25
26
|
|
|
26
27
|
__all__ = [
|
|
@@ -37,6 +38,7 @@ __all__ = [
|
|
|
37
38
|
"CORSConfig",
|
|
38
39
|
"HttpResolverLocal",
|
|
39
40
|
"LambdaFunctionUrlResolver",
|
|
41
|
+
"Request",
|
|
40
42
|
"Response",
|
|
41
43
|
"VPCLatticeResolver",
|
|
42
44
|
"VPCLatticeV2Resolver",
|
|
@@ -42,6 +42,7 @@ from aws_lambda_powertools.event_handler.openapi.types import (
|
|
|
42
42
|
validation_error_definition,
|
|
43
43
|
validation_error_response_definition,
|
|
44
44
|
)
|
|
45
|
+
from aws_lambda_powertools.event_handler.request import Request
|
|
45
46
|
from aws_lambda_powertools.event_handler.util import (
|
|
46
47
|
_FrozenDict,
|
|
47
48
|
_FrozenListDict,
|
|
@@ -466,6 +467,11 @@ class Route:
|
|
|
466
467
|
|
|
467
468
|
self.custom_response_validation_http_code = custom_response_validation_http_code
|
|
468
469
|
|
|
470
|
+
# Caches the name of any Request-typed parameter in the handler.
|
|
471
|
+
# Avoids re-scanning the signature on every invocation.
|
|
472
|
+
self.request_param_name: str | None = None
|
|
473
|
+
self.request_param_name_checked: bool = False
|
|
474
|
+
|
|
469
475
|
def __call__(
|
|
470
476
|
self,
|
|
471
477
|
router_middlewares: list[Callable],
|
|
@@ -1608,6 +1614,47 @@ class BaseRouter(ABC):
|
|
|
1608
1614
|
"""Resets routing context"""
|
|
1609
1615
|
self.context.clear()
|
|
1610
1616
|
|
|
1617
|
+
@property
|
|
1618
|
+
def request(self) -> Request:
|
|
1619
|
+
"""Current resolved :class:`Request` object.
|
|
1620
|
+
|
|
1621
|
+
Available inside middleware and in route handlers that declare a parameter
|
|
1622
|
+
typed as :class:`Request <aws_lambda_powertools.event_handler.request.Request>`.
|
|
1623
|
+
|
|
1624
|
+
Raises
|
|
1625
|
+
------
|
|
1626
|
+
RuntimeError
|
|
1627
|
+
When accessed before route resolution (i.e. outside of middleware / handler scope).
|
|
1628
|
+
|
|
1629
|
+
Examples
|
|
1630
|
+
--------
|
|
1631
|
+
**Middleware**
|
|
1632
|
+
|
|
1633
|
+
```python
|
|
1634
|
+
def my_middleware(app, next_middleware):
|
|
1635
|
+
req = app.request
|
|
1636
|
+
print(req.route, req.method, req.path_parameters)
|
|
1637
|
+
return next_middleware(app)
|
|
1638
|
+
```
|
|
1639
|
+
"""
|
|
1640
|
+
cached: Request | None = self.context.get("_request")
|
|
1641
|
+
if cached is not None:
|
|
1642
|
+
return cached
|
|
1643
|
+
|
|
1644
|
+
route: Route | None = self.context.get("_route")
|
|
1645
|
+
if route is None:
|
|
1646
|
+
raise RuntimeError(
|
|
1647
|
+
"app.request is only available after route resolution. Use it inside middleware or a route handler.",
|
|
1648
|
+
)
|
|
1649
|
+
|
|
1650
|
+
request = Request(
|
|
1651
|
+
route_path=route.openapi_path,
|
|
1652
|
+
path_parameters=self.context.get("_route_args", {}),
|
|
1653
|
+
current_event=self.current_event,
|
|
1654
|
+
)
|
|
1655
|
+
self.context["_request"] = request
|
|
1656
|
+
return request
|
|
1657
|
+
|
|
1611
1658
|
|
|
1612
1659
|
class MiddlewareFrame:
|
|
1613
1660
|
"""
|
|
@@ -1680,6 +1727,24 @@ class MiddlewareFrame:
|
|
|
1680
1727
|
return self.current_middleware(app, self.next_middleware)
|
|
1681
1728
|
|
|
1682
1729
|
|
|
1730
|
+
def _find_request_param_name(func: Callable) -> str | None:
|
|
1731
|
+
"""Return the name of the first parameter annotated as ``Request``, or ``None``."""
|
|
1732
|
+
from typing import get_type_hints
|
|
1733
|
+
|
|
1734
|
+
try:
|
|
1735
|
+
# get_type_hints resolves string annotations from ``from __future__ import annotations``
|
|
1736
|
+
# using the function's own module globals.
|
|
1737
|
+
hints = get_type_hints(func)
|
|
1738
|
+
except Exception:
|
|
1739
|
+
hints = {}
|
|
1740
|
+
|
|
1741
|
+
for param_name, annotation in hints.items():
|
|
1742
|
+
if annotation is Request:
|
|
1743
|
+
return param_name
|
|
1744
|
+
|
|
1745
|
+
return None
|
|
1746
|
+
|
|
1747
|
+
|
|
1683
1748
|
def _registered_api_adapter(
|
|
1684
1749
|
app: ApiGatewayResolver,
|
|
1685
1750
|
next_middleware: Callable[..., Any],
|
|
@@ -1708,6 +1773,17 @@ def _registered_api_adapter(
|
|
|
1708
1773
|
"""
|
|
1709
1774
|
route_args: dict = app.context.get("_route_args", {})
|
|
1710
1775
|
logger.debug(f"Calling API Route Handler: {route_args}")
|
|
1776
|
+
|
|
1777
|
+
# Inject a Request object when the handler declares a parameter typed as Request.
|
|
1778
|
+
# Lookup is cached on the Route object to avoid repeated signature inspection.
|
|
1779
|
+
route: Route | None = app.context.get("_route")
|
|
1780
|
+
if route is not None:
|
|
1781
|
+
if not route.request_param_name_checked:
|
|
1782
|
+
route.request_param_name = _find_request_param_name(next_middleware)
|
|
1783
|
+
route.request_param_name_checked = True
|
|
1784
|
+
if route.request_param_name:
|
|
1785
|
+
route_args = {**route_args, route.request_param_name: app.request}
|
|
1786
|
+
|
|
1711
1787
|
return app._to_response(next_middleware(**route_args))
|
|
1712
1788
|
|
|
1713
1789
|
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import base64
|
|
5
5
|
import inspect
|
|
6
|
+
import threading
|
|
6
7
|
import warnings
|
|
7
8
|
from typing import TYPE_CHECKING, Any, Callable
|
|
8
9
|
from urllib.parse import parse_qs
|
|
@@ -324,36 +325,65 @@ class HttpResolverLocal(ApiGatewayResolver):
|
|
|
324
325
|
return await next_handler(self)
|
|
325
326
|
|
|
326
327
|
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
|
+
"""
|
|
328
336
|
|
|
329
337
|
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
|
|
345
338
|
if inspect.iscoroutinefunction(middleware):
|
|
346
|
-
|
|
347
|
-
else:
|
|
348
|
-
# Sync middleware - need special handling
|
|
349
|
-
result = middleware(app, sync_next)
|
|
339
|
+
return await middleware(app, next_handler)
|
|
350
340
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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 = []
|
|
355
349
|
|
|
356
|
-
|
|
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]
|
|
357
387
|
|
|
358
388
|
return wrapped
|
|
359
389
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import dataclasses
|
|
4
5
|
import json
|
|
5
6
|
import logging
|
|
6
|
-
|
|
7
|
+
import warnings
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping, MutableMapping, Sequence, Union, cast
|
|
7
9
|
from urllib.parse import parse_qs
|
|
8
10
|
|
|
9
11
|
from pydantic import BaseModel
|
|
12
|
+
from typing_extensions import get_args, get_origin
|
|
10
13
|
|
|
11
14
|
from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler
|
|
12
15
|
from aws_lambda_powertools.event_handler.openapi.compat import (
|
|
@@ -24,7 +27,8 @@ from aws_lambda_powertools.event_handler.openapi.exceptions import (
|
|
|
24
27
|
RequestValidationError,
|
|
25
28
|
ResponseValidationError,
|
|
26
29
|
)
|
|
27
|
-
from aws_lambda_powertools.event_handler.openapi.params import Param
|
|
30
|
+
from aws_lambda_powertools.event_handler.openapi.params import Param, UploadFile
|
|
31
|
+
from aws_lambda_powertools.event_handler.openapi.types import UnionType
|
|
28
32
|
|
|
29
33
|
if TYPE_CHECKING:
|
|
30
34
|
from pydantic.fields import FieldInfo
|
|
@@ -42,6 +46,7 @@ logger = logging.getLogger(__name__)
|
|
|
42
46
|
CONTENT_DISPOSITION_NAME_PARAM = "name="
|
|
43
47
|
APPLICATION_JSON_CONTENT_TYPE = "application/json"
|
|
44
48
|
APPLICATION_FORM_CONTENT_TYPE = "application/x-www-form-urlencoded"
|
|
49
|
+
MULTIPART_FORM_DATA_CONTENT_TYPE = "multipart/form-data"
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
class OpenAPIRequestValidationMiddleware(BaseMiddlewareHandler):
|
|
@@ -94,10 +99,17 @@ class OpenAPIRequestValidationMiddleware(BaseMiddlewareHandler):
|
|
|
94
99
|
headers,
|
|
95
100
|
)
|
|
96
101
|
|
|
102
|
+
# Process cookie values
|
|
103
|
+
cookie_values, cookie_errors = _request_params_to_args(
|
|
104
|
+
route.dependant.cookie_params,
|
|
105
|
+
app.current_event.resolved_cookies_field,
|
|
106
|
+
)
|
|
107
|
+
|
|
97
108
|
values.update(path_values)
|
|
98
109
|
values.update(query_values)
|
|
99
110
|
values.update(header_values)
|
|
100
|
-
|
|
111
|
+
values.update(cookie_values)
|
|
112
|
+
errors += path_errors + query_errors + header_errors + cookie_errors
|
|
101
113
|
|
|
102
114
|
# Process the request body, if it exists
|
|
103
115
|
if route.dependant.body_params:
|
|
@@ -132,14 +144,18 @@ class OpenAPIRequestValidationMiddleware(BaseMiddlewareHandler):
|
|
|
132
144
|
elif content_type.startswith(APPLICATION_FORM_CONTENT_TYPE):
|
|
133
145
|
return self._parse_form_data(app)
|
|
134
146
|
|
|
147
|
+
# Handle multipart/form-data (file uploads)
|
|
148
|
+
elif content_type.startswith(MULTIPART_FORM_DATA_CONTENT_TYPE):
|
|
149
|
+
return self._parse_multipart_data(app, content_type)
|
|
150
|
+
|
|
135
151
|
else:
|
|
136
152
|
raise RequestUnsupportedContentType(
|
|
137
|
-
"
|
|
153
|
+
"Unsupported content type",
|
|
138
154
|
errors=[
|
|
139
155
|
{
|
|
140
156
|
"type": "unsupported_content_type",
|
|
141
157
|
"loc": ("body",),
|
|
142
|
-
"msg": "
|
|
158
|
+
"msg": f"Unsupported content type: {content_type}",
|
|
143
159
|
"input": {},
|
|
144
160
|
"ctx": {},
|
|
145
161
|
},
|
|
@@ -186,6 +202,49 @@ class OpenAPIRequestValidationMiddleware(BaseMiddlewareHandler):
|
|
|
186
202
|
],
|
|
187
203
|
) from e
|
|
188
204
|
|
|
205
|
+
def _parse_multipart_data(self, app: EventHandlerInstance, content_type: str) -> dict[str, Any]:
|
|
206
|
+
"""Parse multipart/form-data from the request body (file uploads)."""
|
|
207
|
+
try:
|
|
208
|
+
# Extract the boundary from the content-type header
|
|
209
|
+
boundary = _extract_multipart_boundary(content_type)
|
|
210
|
+
if not boundary:
|
|
211
|
+
raise ValueError("Missing boundary in multipart/form-data content-type header")
|
|
212
|
+
|
|
213
|
+
# Get raw body bytes
|
|
214
|
+
raw_body = app.current_event.body or ""
|
|
215
|
+
if app.current_event.is_base64_encoded:
|
|
216
|
+
body_bytes = base64.b64decode(raw_body)
|
|
217
|
+
else:
|
|
218
|
+
warnings.warn(
|
|
219
|
+
"Received multipart/form-data without base64 encoding. "
|
|
220
|
+
"Binary file uploads may be corrupted. "
|
|
221
|
+
"If using API Gateway REST API (v1), configure Binary Media Types "
|
|
222
|
+
"to include 'multipart/form-data'. "
|
|
223
|
+
"See: https://docs.aws.amazon.com/apigateway/latest/developerguide/"
|
|
224
|
+
"api-gateway-payload-encodings.html",
|
|
225
|
+
stacklevel=2,
|
|
226
|
+
)
|
|
227
|
+
# Use latin-1 to preserve all byte values (0-255) since the body
|
|
228
|
+
# may contain raw binary data that isn't valid UTF-8
|
|
229
|
+
body_bytes = raw_body.encode("latin-1")
|
|
230
|
+
|
|
231
|
+
return _parse_multipart_body(body_bytes, boundary)
|
|
232
|
+
|
|
233
|
+
except ValueError:
|
|
234
|
+
raise
|
|
235
|
+
except Exception as e:
|
|
236
|
+
raise RequestValidationError(
|
|
237
|
+
[
|
|
238
|
+
{
|
|
239
|
+
"type": "multipart_invalid",
|
|
240
|
+
"loc": ("body",),
|
|
241
|
+
"msg": "Multipart form data parsing error",
|
|
242
|
+
"input": {},
|
|
243
|
+
"ctx": {"error": str(e)},
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
) from e
|
|
247
|
+
|
|
189
248
|
|
|
190
249
|
class OpenAPIResponseValidationMiddleware(BaseMiddlewareHandler):
|
|
191
250
|
"""
|
|
@@ -228,20 +287,27 @@ class OpenAPIResponseValidationMiddleware(BaseMiddlewareHandler):
|
|
|
228
287
|
return self._handle_response(route=route, response=response)
|
|
229
288
|
|
|
230
289
|
def _handle_response(self, *, route: Route, response: Response):
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
290
|
+
field = route.dependant.return_param
|
|
291
|
+
|
|
292
|
+
if field is None:
|
|
293
|
+
if not response.is_json():
|
|
294
|
+
return response
|
|
295
|
+
else:
|
|
296
|
+
# JSON serialize the body without validation
|
|
297
|
+
response.body = jsonable_encoder(response.body, custom_serializer=self._validation_serializer)
|
|
298
|
+
else:
|
|
299
|
+
response.body = self._serialize_response_with_validation(
|
|
300
|
+
field=field,
|
|
235
301
|
response_content=response.body,
|
|
236
302
|
has_route_custom_response_validation=route.custom_response_validation_http_code is not None,
|
|
237
303
|
)
|
|
238
304
|
|
|
239
305
|
return response
|
|
240
306
|
|
|
241
|
-
def
|
|
307
|
+
def _serialize_response_with_validation(
|
|
242
308
|
self,
|
|
243
309
|
*,
|
|
244
|
-
field: ModelField
|
|
310
|
+
field: ModelField,
|
|
245
311
|
response_content: Any,
|
|
246
312
|
include: IncEx | None = None,
|
|
247
313
|
exclude: IncEx | None = None,
|
|
@@ -254,33 +320,23 @@ class OpenAPIResponseValidationMiddleware(BaseMiddlewareHandler):
|
|
|
254
320
|
"""
|
|
255
321
|
Serialize the response content according to the field type.
|
|
256
322
|
"""
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
source="route",
|
|
267
|
-
)
|
|
268
|
-
if self._has_response_validation_error:
|
|
269
|
-
raise ResponseValidationError(errors=_normalize_errors(errors), body=response_content, source="app")
|
|
270
|
-
|
|
271
|
-
raise RequestValidationError(errors=_normalize_errors(errors), body=response_content)
|
|
272
|
-
|
|
273
|
-
if hasattr(field, "serialize"):
|
|
274
|
-
return field.serialize(
|
|
275
|
-
value,
|
|
276
|
-
include=include,
|
|
277
|
-
exclude=exclude,
|
|
278
|
-
by_alias=by_alias,
|
|
279
|
-
exclude_unset=exclude_unset,
|
|
280
|
-
exclude_defaults=exclude_defaults,
|
|
281
|
-
exclude_none=exclude_none,
|
|
323
|
+
errors: list[dict[str, Any]] = []
|
|
324
|
+
value = _validate_field(field=field, value=response_content, loc=("response",), existing_errors=errors)
|
|
325
|
+
if errors:
|
|
326
|
+
# route-level validation must take precedence over app-level
|
|
327
|
+
if has_route_custom_response_validation:
|
|
328
|
+
raise ResponseValidationError(
|
|
329
|
+
errors=_normalize_errors(errors),
|
|
330
|
+
body=response_content,
|
|
331
|
+
source="route",
|
|
282
332
|
)
|
|
283
|
-
|
|
333
|
+
if self._has_response_validation_error:
|
|
334
|
+
raise ResponseValidationError(errors=_normalize_errors(errors), body=response_content, source="app")
|
|
335
|
+
|
|
336
|
+
raise RequestValidationError(errors=_normalize_errors(errors), body=response_content)
|
|
337
|
+
|
|
338
|
+
if hasattr(field, "serialize"):
|
|
339
|
+
return field.serialize(
|
|
284
340
|
value,
|
|
285
341
|
include=include,
|
|
286
342
|
exclude=exclude,
|
|
@@ -288,11 +344,18 @@ class OpenAPIResponseValidationMiddleware(BaseMiddlewareHandler):
|
|
|
288
344
|
exclude_unset=exclude_unset,
|
|
289
345
|
exclude_defaults=exclude_defaults,
|
|
290
346
|
exclude_none=exclude_none,
|
|
291
|
-
custom_serializer=self._validation_serializer,
|
|
292
347
|
)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
348
|
+
|
|
349
|
+
return jsonable_encoder(
|
|
350
|
+
value,
|
|
351
|
+
include=include,
|
|
352
|
+
exclude=exclude,
|
|
353
|
+
by_alias=by_alias,
|
|
354
|
+
exclude_unset=exclude_unset,
|
|
355
|
+
exclude_defaults=exclude_defaults,
|
|
356
|
+
exclude_none=exclude_none,
|
|
357
|
+
custom_serializer=self._validation_serializer,
|
|
358
|
+
)
|
|
296
359
|
|
|
297
360
|
def _prepare_response_content(
|
|
298
361
|
self,
|
|
@@ -385,7 +448,12 @@ def _request_body_to_args(
|
|
|
385
448
|
continue
|
|
386
449
|
|
|
387
450
|
value = _normalize_field_value(value=value, field_info=field.field_info)
|
|
388
|
-
|
|
451
|
+
|
|
452
|
+
# UploadFile objects bypass Pydantic validation — they're already constructed
|
|
453
|
+
if isinstance(value, UploadFile):
|
|
454
|
+
values[field.name] = value
|
|
455
|
+
else:
|
|
456
|
+
values[field.name] = _validate_field(field=field, value=value, loc=loc, existing_errors=errors)
|
|
389
457
|
|
|
390
458
|
return values, errors
|
|
391
459
|
|
|
@@ -427,9 +495,45 @@ def _handle_missing_field_value(
|
|
|
427
495
|
values[field.name] = field.get_default()
|
|
428
496
|
|
|
429
497
|
|
|
498
|
+
def _is_or_contains_sequence(annotation: Any) -> bool:
|
|
499
|
+
"""
|
|
500
|
+
Check if annotation is a sequence or Union/RootModel containing a sequence.
|
|
501
|
+
|
|
502
|
+
This function handles complex type annotations like:
|
|
503
|
+
- List[Model] - direct sequence
|
|
504
|
+
- Union[Model, List[Model]] - checks if any Union member is a sequence
|
|
505
|
+
- Optional[List[Model]] - Union[List[Model], None]
|
|
506
|
+
- RootModel[List[Model]] - checks if the RootModel wraps a sequence
|
|
507
|
+
- Optional[RootModel[List[Model]]] - Union member that is a RootModel
|
|
508
|
+
- RootModel[Union[Model, List[Model]]] - RootModel wrapping a Union with a sequence
|
|
509
|
+
"""
|
|
510
|
+
# Direct sequence check
|
|
511
|
+
if field_annotation_is_sequence(annotation):
|
|
512
|
+
return True
|
|
513
|
+
|
|
514
|
+
# Check Union members — recurse so we catch RootModel inside Union
|
|
515
|
+
origin = get_origin(annotation)
|
|
516
|
+
if origin is Union or origin is UnionType:
|
|
517
|
+
for arg in get_args(annotation):
|
|
518
|
+
if _is_or_contains_sequence(arg):
|
|
519
|
+
return True
|
|
520
|
+
|
|
521
|
+
# Check if it's a RootModel wrapping a sequence (or Union containing a sequence)
|
|
522
|
+
if lenient_issubclass(annotation, BaseModel) and getattr(annotation, "__pydantic_root_model__", False):
|
|
523
|
+
if hasattr(annotation, "model_fields") and "root" in annotation.model_fields:
|
|
524
|
+
root_annotation = annotation.model_fields["root"].annotation
|
|
525
|
+
return _is_or_contains_sequence(root_annotation)
|
|
526
|
+
|
|
527
|
+
return False
|
|
528
|
+
|
|
529
|
+
|
|
430
530
|
def _normalize_field_value(value: Any, field_info: FieldInfo) -> Any:
|
|
431
531
|
"""Normalize field value, converting lists to single values for non-sequence fields."""
|
|
432
|
-
|
|
532
|
+
# When annotation is bytes but value is UploadFile, extract raw content
|
|
533
|
+
if isinstance(value, UploadFile) and field_info.annotation is bytes:
|
|
534
|
+
return value.content
|
|
535
|
+
|
|
536
|
+
if _is_or_contains_sequence(field_info.annotation):
|
|
433
537
|
return value
|
|
434
538
|
elif isinstance(value, list) and value:
|
|
435
539
|
return value[0]
|
|
@@ -542,3 +646,106 @@ def _get_param_value(
|
|
|
542
646
|
value = input_dict.get(field_name)
|
|
543
647
|
|
|
544
648
|
return value
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _extract_multipart_boundary(content_type: str) -> str | None:
|
|
652
|
+
"""Extract the boundary string from a multipart/form-data content-type header."""
|
|
653
|
+
for segment in content_type.split(";"):
|
|
654
|
+
stripped = segment.strip()
|
|
655
|
+
if stripped.startswith("boundary="):
|
|
656
|
+
boundary = stripped[len("boundary=") :]
|
|
657
|
+
# Remove optional quotes around boundary
|
|
658
|
+
if boundary.startswith('"') and boundary.endswith('"'):
|
|
659
|
+
boundary = boundary[1:-1]
|
|
660
|
+
return boundary
|
|
661
|
+
return None
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def _parse_multipart_body(body: bytes, boundary: str) -> dict[str, Any]:
|
|
665
|
+
"""
|
|
666
|
+
Parse a multipart/form-data body into a dict of field names to values.
|
|
667
|
+
|
|
668
|
+
File fields get bytes values; regular form fields get string values.
|
|
669
|
+
Multiple values for the same field name are collected into lists.
|
|
670
|
+
"""
|
|
671
|
+
delimiter = f"--{boundary}".encode()
|
|
672
|
+
end_delimiter = f"--{boundary}--".encode()
|
|
673
|
+
|
|
674
|
+
result: dict[str, Any] = {}
|
|
675
|
+
|
|
676
|
+
# Split body by the boundary delimiter
|
|
677
|
+
raw_parts = body.split(delimiter)
|
|
678
|
+
|
|
679
|
+
for raw_part in raw_parts:
|
|
680
|
+
# Skip the preamble (before first boundary) and epilogue (after closing boundary)
|
|
681
|
+
if not raw_part or raw_part.strip() == b"" or raw_part.strip() == b"--":
|
|
682
|
+
continue
|
|
683
|
+
|
|
684
|
+
# Remove the end delimiter marker if present
|
|
685
|
+
chunk = raw_part
|
|
686
|
+
if chunk.endswith(end_delimiter):
|
|
687
|
+
chunk = chunk[: -len(end_delimiter)]
|
|
688
|
+
|
|
689
|
+
# Strip leading \r\n
|
|
690
|
+
if chunk.startswith(b"\r\n"):
|
|
691
|
+
chunk = chunk[2:]
|
|
692
|
+
|
|
693
|
+
# Strip trailing \r\n
|
|
694
|
+
if chunk.endswith(b"\r\n"):
|
|
695
|
+
chunk = chunk[:-2]
|
|
696
|
+
|
|
697
|
+
# Split headers from body at the double CRLF
|
|
698
|
+
header_end = chunk.find(b"\r\n\r\n")
|
|
699
|
+
if header_end == -1:
|
|
700
|
+
continue
|
|
701
|
+
|
|
702
|
+
header_section = chunk[:header_end].decode("utf-8")
|
|
703
|
+
body_section = chunk[header_end + 4 :]
|
|
704
|
+
|
|
705
|
+
# Parse Content-Disposition to get the field name and optional filename
|
|
706
|
+
field_name = None
|
|
707
|
+
filename = None
|
|
708
|
+
content_type_header = None
|
|
709
|
+
|
|
710
|
+
for header_line in header_section.split("\r\n"):
|
|
711
|
+
header_lower = header_line.lower()
|
|
712
|
+
if header_lower.startswith("content-disposition:"):
|
|
713
|
+
field_name = _extract_header_param(header_line, "name")
|
|
714
|
+
filename = _extract_header_param(header_line, "filename")
|
|
715
|
+
elif header_lower.startswith("content-type:"):
|
|
716
|
+
content_type_header = header_line.split(":", 1)[1].strip()
|
|
717
|
+
|
|
718
|
+
if field_name is None:
|
|
719
|
+
continue
|
|
720
|
+
|
|
721
|
+
# If it has a filename, it's a file upload — wrap as UploadFile
|
|
722
|
+
# Otherwise it's a regular form field — decode to string
|
|
723
|
+
if filename is not None:
|
|
724
|
+
value: Any = UploadFile(content=body_section, filename=filename, content_type=content_type_header)
|
|
725
|
+
else:
|
|
726
|
+
value = body_section.decode("utf-8")
|
|
727
|
+
|
|
728
|
+
# Collect multiple values for same field name into a list
|
|
729
|
+
if field_name in result:
|
|
730
|
+
existing = result[field_name]
|
|
731
|
+
if isinstance(existing, list):
|
|
732
|
+
existing.append(value)
|
|
733
|
+
else:
|
|
734
|
+
result[field_name] = [existing, value]
|
|
735
|
+
else:
|
|
736
|
+
result[field_name] = value
|
|
737
|
+
|
|
738
|
+
return result
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def _extract_header_param(header_line: str, param_name: str) -> str | None:
|
|
742
|
+
"""Extract a parameter value from a header line (e.g., name="file" from Content-Disposition)."""
|
|
743
|
+
search = f'{param_name}="'
|
|
744
|
+
idx = header_line.find(search)
|
|
745
|
+
if idx == -1:
|
|
746
|
+
return None
|
|
747
|
+
start = idx + len(search)
|
|
748
|
+
end = header_line.find('"', start)
|
|
749
|
+
if end == -1:
|
|
750
|
+
return None
|
|
751
|
+
return header_line[start:end]
|
|
@@ -13,15 +13,16 @@ from aws_lambda_powertools.event_handler.openapi.compat import (
|
|
|
13
13
|
from aws_lambda_powertools.event_handler.openapi.params import (
|
|
14
14
|
Body,
|
|
15
15
|
Dependant,
|
|
16
|
+
File,
|
|
16
17
|
Form,
|
|
17
18
|
Param,
|
|
18
19
|
ParamTypes,
|
|
19
|
-
_File,
|
|
20
20
|
analyze_param,
|
|
21
21
|
create_response_field,
|
|
22
22
|
get_flat_dependant,
|
|
23
23
|
)
|
|
24
24
|
from aws_lambda_powertools.event_handler.openapi.types import OpenAPIResponse, OpenAPIResponseContentModel
|
|
25
|
+
from aws_lambda_powertools.event_handler.request import Request
|
|
25
26
|
|
|
26
27
|
if TYPE_CHECKING:
|
|
27
28
|
from collections.abc import Callable
|
|
@@ -187,6 +188,11 @@ def get_dependant(
|
|
|
187
188
|
|
|
188
189
|
# Add each parameter to the dependant model
|
|
189
190
|
for param_name, param in signature_params.items():
|
|
191
|
+
# Request-typed parameters are injected by the resolver at call time;
|
|
192
|
+
# they carry no OpenAPI meaning and must be excluded from schema generation.
|
|
193
|
+
if param.annotation is Request:
|
|
194
|
+
continue
|
|
195
|
+
|
|
190
196
|
# If the parameter is a path parameter, we need to set the in_ field to "path".
|
|
191
197
|
is_path_param = param_name in path_param_names
|
|
192
198
|
|
|
@@ -364,9 +370,9 @@ def get_body_field_info(
|
|
|
364
370
|
if not required:
|
|
365
371
|
body_field_info_kwargs["default"] = None
|
|
366
372
|
|
|
367
|
-
if any(isinstance(f.field_info,
|
|
368
|
-
|
|
369
|
-
|
|
373
|
+
if any(isinstance(f.field_info, File) for f in flat_dependant.body_params):
|
|
374
|
+
body_field_info = Body
|
|
375
|
+
body_field_info_kwargs["media_type"] = "multipart/form-data"
|
|
370
376
|
elif any(isinstance(f.field_info, Form) for f in flat_dependant.body_params):
|
|
371
377
|
body_field_info = Body
|
|
372
378
|
body_field_info_kwargs["media_type"] = "application/x-www-form-urlencoded"
|