aws-lambda-powertools 3.30.0__tar.gz → 3.31.1__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.
Files changed (320) hide show
  1. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/PKG-INFO +3 -3
  2. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py +1 -1
  3. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/dependant.py +8 -1
  4. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/params.py +45 -43
  5. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/constants.py +3 -0
  6. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/version.py +1 -1
  7. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py +35 -0
  8. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/base.py +284 -0
  9. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py +119 -0
  10. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/config.py +184 -0
  11. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/exceptions.py +77 -0
  12. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py +15 -0
  13. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/base.py +327 -0
  14. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/dynamodb.py +293 -0
  15. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/record.py +64 -0
  16. aws_lambda_powertools-3.31.1/aws_lambda_powertools/utilities/circuit_breaker_alpha/states.py +126 -0
  17. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/event_bridge_event.py +1 -1
  18. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/event_source.py +24 -5
  19. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/dynamodb.py +2 -1
  20. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/pyproject.toml +3 -3
  21. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/setup.py +6 -4
  22. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/LICENSE +0 -0
  23. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/README.md +0 -0
  24. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/THIRD-PARTY-LICENSES +0 -0
  25. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/__init__.py +0 -0
  26. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/__init__.py +0 -0
  27. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/api_gateway.py +0 -0
  28. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/appsync.py +0 -0
  29. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/bedrock_agent.py +0 -0
  30. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/bedrock_agent_function.py +0 -0
  31. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/content_types.py +0 -0
  32. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/depends.py +0 -0
  33. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/__init__.py +0 -0
  34. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/_registry.py +0 -0
  35. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/base.py +0 -0
  36. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/exceptions.py +0 -0
  37. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/functions.py +0 -0
  38. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/router.py +0 -0
  39. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/types.py +0 -0
  40. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/exception_handling.py +0 -0
  41. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/exceptions.py +0 -0
  42. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/__init__.py +0 -0
  43. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py +0 -0
  44. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/base.py +0 -0
  45. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py +0 -0
  46. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/router.py +0 -0
  47. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/http_resolver.py +0 -0
  48. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/lambda_function_url.py +0 -0
  49. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/__init__.py +0 -0
  50. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/async_utils.py +0 -0
  51. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/base.py +0 -0
  52. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py +0 -0
  53. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/schema_validation.py +0 -0
  54. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/__init__.py +0 -0
  55. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/compat.py +0 -0
  56. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/config.py +0 -0
  57. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/constants.py +0 -0
  58. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/encoders.py +0 -0
  59. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/exceptions.py +0 -0
  60. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/merge.py +0 -0
  61. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/models.py +0 -0
  62. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py +0 -0
  63. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/schema_generator.py +0 -0
  64. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py +0 -0
  65. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py +0 -0
  66. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py +0 -0
  67. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js +0 -0
  68. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui.min.css +0 -0
  69. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/types.py +0 -0
  70. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/request.py +0 -0
  71. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/router.py +0 -0
  72. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/types.py +0 -0
  73. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/util.py +0 -0
  74. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/vpc_lattice.py +0 -0
  75. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/exceptions/__init__.py +0 -0
  76. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/__init__.py +0 -0
  77. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/__init__.py +0 -0
  78. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/cache.py +0 -0
  79. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/config.py +0 -0
  80. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/functions.py +0 -0
  81. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/handler.py +0 -0
  82. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/constants.py +0 -0
  83. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/correlation_paths.py +0 -0
  84. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/exceptions.py +0 -0
  85. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/filters.py +0 -0
  86. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatter.py +0 -0
  87. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatters/__init__.py +0 -0
  88. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatters/datadog.py +0 -0
  89. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/lambda_context.py +0 -0
  90. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/logger.py +0 -0
  91. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/types.py +0 -0
  92. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/utils.py +0 -0
  93. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/__init__.py +0 -0
  94. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/base.py +0 -0
  95. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/exceptions.py +0 -0
  96. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/functions.py +0 -0
  97. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/metric.py +0 -0
  98. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/metrics.py +0 -0
  99. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/__init__.py +0 -0
  100. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/base.py +0 -0
  101. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/__init__.py +0 -0
  102. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py +0 -0
  103. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/constants.py +0 -0
  104. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/exceptions.py +0 -0
  105. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/metric_properties.py +0 -0
  106. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/types.py +0 -0
  107. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cold_start.py +0 -0
  108. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/__init__.py +0 -0
  109. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/datadog.py +0 -0
  110. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/metrics.py +0 -0
  111. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/warnings.py +0 -0
  112. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/types.py +0 -0
  113. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/__init__.py +0 -0
  114. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/exceptions.py +0 -0
  115. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/factory.py +0 -0
  116. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/package_logger.py +0 -0
  117. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/py.typed +0 -0
  118. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/__init__.py +0 -0
  119. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/cache_dict.py +0 -0
  120. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/cookies.py +0 -0
  121. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/dynamodb_deserializer.py +0 -0
  122. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/functions.py +0 -0
  123. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/headers_serializer.py +0 -0
  124. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/json_encoder.py +0 -0
  125. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/lazy_import.py +0 -0
  126. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/types.py +0 -0
  127. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/user_agent.py +0 -0
  128. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/__init__.py +0 -0
  129. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/base.py +0 -0
  130. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/extensions.py +0 -0
  131. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/tracer.py +0 -0
  132. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/__init__.py +0 -0
  133. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/__init__.py +0 -0
  134. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/base.py +0 -0
  135. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/decorators.py +0 -0
  136. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/exceptions.py +0 -0
  137. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py +0 -0
  138. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/types.py +0 -0
  139. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/__init__.py +0 -0
  140. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/active_mq_event.py +0 -0
  141. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/alb_event.py +0 -0
  142. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/api_gateway_authorizer_event.py +0 -0
  143. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/api_gateway_proxy_event.py +0 -0
  144. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/api_gateway_websocket_event.py +0 -0
  145. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync/__init__.py +0 -0
  146. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync/scalar_types_utils.py +0 -0
  147. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync_authorizer_event.py +0 -0
  148. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync_resolver_event.py +0 -0
  149. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync_resolver_events_event.py +0 -0
  150. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/aws_config_rule_event.py +0 -0
  151. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py +0 -0
  152. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/bedrock_agent_function_event.py +0 -0
  153. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py +0 -0
  154. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/cloud_watch_custom_widget_event.py +0 -0
  155. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/cloud_watch_logs_event.py +0 -0
  156. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/cloudformation_custom_resource_event.py +0 -0
  157. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/code_deploy_lifecycle_hook_event.py +0 -0
  158. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/code_pipeline_job_event.py +0 -0
  159. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/cognito_user_pool_event.py +0 -0
  160. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/common.py +0 -0
  161. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/connect_contact_flow_event.py +0 -0
  162. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/dynamo_db_stream_event.py +0 -0
  163. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/iot_registry_event.py +0 -0
  164. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kafka_event.py +0 -0
  165. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kinesis_firehose_event.py +0 -0
  166. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kinesis_stream_event.py +0 -0
  167. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/lambda_function_url_event.py +0 -0
  168. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/rabbit_mq_event.py +0 -0
  169. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/s3_batch_operation_event.py +0 -0
  170. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/s3_event.py +0 -0
  171. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/s3_object_event.py +0 -0
  172. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/secrets_manager_event.py +0 -0
  173. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/ses_event.py +0 -0
  174. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/shared_functions.py +0 -0
  175. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/sns_event.py +0 -0
  176. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/sqs_event.py +0 -0
  177. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/transfer_family_event.py +0 -0
  178. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/vpc_lattice.py +0 -0
  179. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/__init__.py +0 -0
  180. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/base.py +0 -0
  181. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/constants.py +0 -0
  182. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/exceptions.py +0 -0
  183. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/__init__.py +0 -0
  184. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/base.py +0 -0
  185. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/kms/__init__.py +0 -0
  186. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/kms/aws_encryption_sdk.py +0 -0
  187. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/__init__.py +0 -0
  188. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/appconfig.py +0 -0
  189. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/base.py +0 -0
  190. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/comparators.py +0 -0
  191. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/constants.py +0 -0
  192. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/exceptions.py +0 -0
  193. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/feature_flags.py +0 -0
  194. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/schema.py +0 -0
  195. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/types.py +0 -0
  196. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/__init__.py +0 -0
  197. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/base.py +0 -0
  198. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/config.py +0 -0
  199. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/exceptions.py +0 -0
  200. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/hook.py +0 -0
  201. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/idempotency.py +0 -0
  202. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/__init__.py +0 -0
  203. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/base.py +0 -0
  204. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/cache.py +0 -0
  205. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/datarecord.py +0 -0
  206. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py +0 -0
  207. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/redis.py +0 -0
  208. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/__init__.py +0 -0
  209. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/base.py +0 -0
  210. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/custom_dict.py +0 -0
  211. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/dataclass.py +0 -0
  212. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/functions.py +0 -0
  213. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/no_op.py +0 -0
  214. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/pydantic.py +0 -0
  215. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/jmespath_utils/__init__.py +0 -0
  216. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/jmespath_utils/envelopes.py +0 -0
  217. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/__init__.py +0 -0
  218. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/consumer_records.py +0 -0
  219. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/__init__.py +0 -0
  220. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/avro.py +0 -0
  221. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/base.py +0 -0
  222. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/default.py +0 -0
  223. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/deserializer.py +0 -0
  224. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/json.py +0 -0
  225. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/protobuf.py +0 -0
  226. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/exceptions.py +0 -0
  227. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/kafka_consumer.py +0 -0
  228. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/schema_config.py +0 -0
  229. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/__init__.py +0 -0
  230. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/base.py +0 -0
  231. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/custom_dict.py +0 -0
  232. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/dataclass.py +0 -0
  233. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/pydantic.py +0 -0
  234. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/serialization.py +0 -0
  235. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/types.py +0 -0
  236. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/__init__.py +0 -0
  237. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/exceptions.py +0 -0
  238. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/lambda_metadata.py +0 -0
  239. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/__init__.py +0 -0
  240. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/appconfig.py +0 -0
  241. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/base.py +0 -0
  242. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/constants.py +0 -0
  243. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/dynamodb.py +0 -0
  244. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/exceptions.py +0 -0
  245. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/secrets.py +0 -0
  246. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/ssm.py +0 -0
  247. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/types.py +0 -0
  248. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/__init__.py +0 -0
  249. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/__init__.py +0 -0
  250. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigw.py +0 -0
  251. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigw_websocket.py +0 -0
  252. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigwv2.py +0 -0
  253. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/base.py +0 -0
  254. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/bedrock_agent.py +0 -0
  255. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/cloudwatch.py +0 -0
  256. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/dynamodb.py +0 -0
  257. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/event_bridge.py +0 -0
  258. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kafka.py +0 -0
  259. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kinesis.py +0 -0
  260. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kinesis_firehose.py +0 -0
  261. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/lambda_function_url.py +0 -0
  262. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/sns.py +0 -0
  263. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/sqs.py +0 -0
  264. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/vpc_lattice.py +0 -0
  265. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/vpc_latticev2.py +0 -0
  266. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/exceptions.py +0 -0
  267. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/functions.py +0 -0
  268. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/__init__.py +0 -0
  269. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/alb.py +0 -0
  270. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigw.py +0 -0
  271. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigw_websocket.py +0 -0
  272. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigwv2.py +0 -0
  273. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/appsync.py +0 -0
  274. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/appsync_events.py +0 -0
  275. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/bedrock_agent.py +0 -0
  276. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cloudformation_custom_resource.py +0 -0
  277. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cloudwatch.py +0 -0
  278. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cognito.py +0 -0
  279. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/event_bridge.py +0 -0
  280. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/iot_registry_events.py +0 -0
  281. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kafka.py +0 -0
  282. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis.py +0 -0
  283. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py +0 -0
  284. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py +0 -0
  285. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/lambda_function_url.py +0 -0
  286. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3.py +0 -0
  287. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_batch_operation.py +0 -0
  288. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +0 -0
  289. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_object_event.py +0 -0
  290. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/ses.py +0 -0
  291. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/sns.py +0 -0
  292. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/sqs.py +0 -0
  293. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/transfer_family.py +0 -0
  294. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/vpc_lattice.py +0 -0
  295. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/vpc_latticev2.py +0 -0
  296. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/parser.py +0 -0
  297. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/types.py +0 -0
  298. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/serialization.py +0 -0
  299. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/__init__.py +0 -0
  300. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/_s3_seekable_io.py +0 -0
  301. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/compat.py +0 -0
  302. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/constants.py +0 -0
  303. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/s3_object.py +0 -0
  304. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/__init__.py +0 -0
  305. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/base.py +0 -0
  306. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/csv.py +0 -0
  307. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/gzip.py +0 -0
  308. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/zip.py +0 -0
  309. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/types.py +0 -0
  310. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/__init__.py +0 -0
  311. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_client_context.py +0 -0
  312. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_client_context_mobile_client.py +0 -0
  313. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_cognito_identity.py +0 -0
  314. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_context.py +0 -0
  315. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/__init__.py +0 -0
  316. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/base.py +0 -0
  317. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/envelopes.py +0 -0
  318. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/exceptions.py +0 -0
  319. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/validator.py +0 -0
  320. {aws_lambda_powertools-3.30.0 → aws_lambda_powertools-3.31.1}/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.30.0
3
+ Version: 3.31.1
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
@@ -29,7 +29,7 @@ Provides-Extra: tracer
29
29
  Provides-Extra: validation
30
30
  Provides-Extra: valkey
31
31
  Requires-Dist: avro (>=1.12.0,<2.0.0) ; extra == "kafka-consumer-avro"
32
- Requires-Dist: aws-encryption-sdk (>=3.3.1,!=4.0.0,!=4.0.1,!=4.0.2,!=4.0.3,!=4.0.4,<5.0.0) ; extra == "all" or extra == "datamasking"
32
+ Requires-Dist: aws-encryption-sdk (>=4.0.5,<5.0.0) ; extra == "all" or extra == "datamasking"
33
33
  Requires-Dist: aws-xray-sdk (>=2.8.0,<3.0.0) ; extra == "tracer" or extra == "all"
34
34
  Requires-Dist: boto3 (>=1.34.32,<2.0.0) ; extra == "aws-sdk"
35
35
  Requires-Dist: datadog-lambda (>=8.114.0,<9.0.0) ; extra == "datadog"
@@ -39,7 +39,7 @@ Requires-Dist: jsonpath-ng (>=1.6.0,<2.0.0) ; extra == "all" or extra == "datama
39
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
- Requires-Dist: redis (>=4.4,<8.0) ; extra == "redis"
42
+ Requires-Dist: redis (>=4.4,<9.0) ; extra == "redis"
43
43
  Requires-Dist: typing-extensions (>=4.11.0,<5.0.0)
44
44
  Requires-Dist: valkey-glide (>=1.3.5,<3.0) ; extra == "valkey"
45
45
  Project-URL: Documentation, https://docs.powertools.aws.dev/lambda/python/
@@ -321,7 +321,7 @@ class AppSyncEventsResolver(Router):
321
321
  if isinstance(ret, Exception)
322
322
  else {"id": e.get("id"), "payload": ret}
323
323
  )
324
- for e, ret in zip(self.current_event.events, results)
324
+ for e, ret in zip(self.current_event.events, results, strict=True)
325
325
  ],
326
326
  )
327
327
 
@@ -156,6 +156,7 @@ def get_dependant(
156
156
  call: Callable[..., Any],
157
157
  name: str | None = None,
158
158
  responses: dict[int, OpenAPIResponse] | None = None,
159
+ is_dependency: bool = False,
159
160
  ) -> Dependant:
160
161
  """
161
162
  Returns a dependant model for a handler function. A dependant model is a model that contains
@@ -200,6 +201,7 @@ def get_dependant(
200
201
  sub_dependant = get_dependant(
201
202
  path=path,
202
203
  call=depends_instance.dependency,
204
+ is_dependency=True,
203
205
  )
204
206
  dependant.dependencies.append(
205
207
  DependencyParam(
@@ -229,7 +231,12 @@ def get_dependant(
229
231
  else:
230
232
  add_param_to_fields(field=param_field, dependant=dependant)
231
233
 
232
- _add_return_annotation(dependant, endpoint_signature)
234
+ # A dependency's return value is injected directly into the handler, never serialized as a
235
+ # response body nor validated as an OpenAPI parameter — so building a Pydantic schema for it is
236
+ # both unused (no reader consumes a sub-dependency's return_param) and actively harmful: it crashes
237
+ # for arbitrary return types (e.g. boto3/botocore clients). Only the top-level handler needs it.
238
+ if not is_dependency:
239
+ _add_return_annotation(dependant, endpoint_signature)
233
240
  _add_extra_responses(dependant, responses)
234
241
 
235
242
  return dependant
@@ -953,54 +953,52 @@ def get_field_info_response_type(annotation, value) -> tuple[FieldInfo | None, A
953
953
  return get_field_info_and_type_annotation(inner_type, value, False, True)
954
954
 
955
955
 
956
- def _has_discriminator(field_info: FieldInfo) -> bool:
957
- """Check if a FieldInfo has a discriminator."""
958
- return hasattr(field_info, "discriminator") and field_info.discriminator is not None
959
-
960
-
961
- def _handle_discriminator_with_param(
956
+ def _split_location_marker_and_field(
962
957
  annotations: list[FieldInfo],
963
- annotation: Any,
964
- ) -> tuple[FieldInfo | None, Any, bool]:
958
+ ) -> tuple[FieldInfo, FieldInfo]:
965
959
  """
966
- Handle the special case of Field(discriminator) + Body() combination.
960
+ Split a pair of FieldInfo into the Powertools location marker and the plain Pydantic Field.
961
+
962
+ A parameter like ``Annotated[str, Field(gt=0), Query()]`` flattens to two FieldInfo: the
963
+ location marker (``Path``/``Query``/``Header``/``Body``, a ``Param`` or ``Body`` subclass) and
964
+ a plain Pydantic ``Field`` carrying constraints. We keep them apart so the location marker
965
+ drives where the value comes from, while the ``Field``'s constraints/metadata still apply.
967
966
 
968
967
  Returns:
969
- tuple of (powertools_annotation, type_annotation, has_discriminator_with_body)
968
+ tuple of (location_marker, plain_field)
970
969
  """
971
- field_obj = None
972
- body_obj = None
970
+ location_marker: FieldInfo | None = None
971
+ plain_field: FieldInfo | None = None
973
972
 
974
973
  for ann in annotations:
975
- if isinstance(ann, Body):
976
- body_obj = ann
977
- elif _has_discriminator(ann):
978
- field_obj = ann
974
+ if isinstance(ann, (Param, Body)):
975
+ location_marker = ann
976
+ else:
977
+ plain_field = ann
979
978
 
980
- if field_obj and body_obj:
981
- # Use Body as the primary annotation, preserve full annotation for validation
982
- return body_obj, annotation, True
979
+ if location_marker is None or plain_field is None:
980
+ raise AssertionError("Only one FieldInfo can be used per parameter")
983
981
 
984
- raise AssertionError("Only one FieldInfo can be used per parameter")
982
+ return location_marker, plain_field
985
983
 
986
984
 
987
985
  def _create_field_info(
988
986
  powertools_annotation: FieldInfo,
989
987
  type_annotation: Any,
990
- has_discriminator_with_body: bool,
988
+ preserve_full_annotation: bool,
991
989
  ) -> FieldInfo:
992
990
  """Create or copy FieldInfo based on the annotation type."""
993
- field_info: FieldInfo
994
- if has_discriminator_with_body:
995
- # For discriminator + Body case, create a new Body instance directly
996
- field_info = Body()
991
+ # Copy field_info because we mutate field_info.default later.
992
+ field_info = copy_field_info(
993
+ field_info=powertools_annotation,
994
+ annotation=type_annotation,
995
+ )
996
+ if preserve_full_annotation:
997
+ # The location marker is paired with a plain Field (constraints or discriminator).
998
+ # copy_field_info strips the Pydantic Field out of the metadata, so re-attach the full
999
+ # Annotated type here, that's what makes the Field's metadata flow into the TypeAdapter
1000
+ # that ModelField builds for validation.
997
1001
  field_info.annotation = type_annotation
998
- else:
999
- # Copy field_info because we mutate field_info.default later
1000
- field_info = copy_field_info(
1001
- field_info=powertools_annotation,
1002
- annotation=type_annotation,
1003
- )
1004
1002
  return field_info
1005
1003
 
1006
1004
 
@@ -1043,13 +1041,16 @@ def get_field_info_annotated_type(annotation, value, is_path_param: bool) -> tup
1043
1041
 
1044
1042
  # Determine which annotation to use
1045
1043
  powertools_annotation: FieldInfo | None = None
1046
- has_discriminator_with_param = False
1044
+ # When a plain Pydantic Field is paired with a location marker (e.g. Field(gt=0) + Query),
1045
+ # we keep the full Annotated type as the annotation so the Field's metadata still validates.
1046
+ preserve_full_annotation = False
1047
1047
 
1048
1048
  if len(powertools_annotations) == 2:
1049
- powertools_annotation, type_annotation, has_discriminator_with_param = _handle_discriminator_with_param(
1050
- powertools_annotations,
1051
- annotation,
1052
- )
1049
+ # A location marker (Path/Query/Header/Body) plus a plain Pydantic Field carrying
1050
+ # constraints, a discriminator, or any other Field setting. The marker says where the
1051
+ # value comes from; the Field says how to validate it.
1052
+ powertools_annotation, _ = _split_location_marker_and_field(powertools_annotations)
1053
+ preserve_full_annotation = True
1053
1054
  elif len(powertools_annotations) > 1:
1054
1055
  raise AssertionError("Only one FieldInfo can be used per parameter")
1055
1056
  else:
@@ -1057,18 +1058,19 @@ def get_field_info_annotated_type(annotation, value, is_path_param: bool) -> tup
1057
1058
 
1058
1059
  # Reconstruct type_annotation with non-FieldInfo metadata if present
1059
1060
  # This ensures constraints like Interval are preserved
1060
- if other_metadata and not has_discriminator_with_param:
1061
+ if other_metadata and not preserve_full_annotation:
1061
1062
  type_annotation = Annotated[(type_annotation, *other_metadata)]
1062
1063
 
1063
1064
  # Process the annotation if it exists
1064
1065
  field_info: FieldInfo | None = None
1065
- if isinstance(powertools_annotation, FieldInfo): # pragma: no cover
1066
- field_info = _create_field_info(powertools_annotation, type_annotation, has_discriminator_with_param)
1066
+ if isinstance(powertools_annotation, FieldInfo):
1067
+ # When pairing a location marker with a plain Field, hand the full Annotated[...] to the
1068
+ # field so the Field's constraints/discriminator flow into the validating TypeAdapter.
1069
+ field_annotation = annotation if preserve_full_annotation else type_annotation
1070
+ field_info = _create_field_info(powertools_annotation, field_annotation, preserve_full_annotation)
1067
1071
  _set_field_default(field_info, value, is_path_param)
1068
-
1069
- # Preserve full annotated type for discriminated unions
1070
- if _has_discriminator(powertools_annotation): # pragma: no cover
1071
- type_annotation = annotation # pragma: no cover
1072
+ if preserve_full_annotation:
1073
+ type_annotation = annotation
1072
1074
 
1073
1075
  return field_info, type_annotation
1074
1076
 
@@ -76,3 +76,6 @@ METADATA_DEFAULT_TIMEOUT_SECS: float = 1.0
76
76
 
77
77
  # Idempotency constants
78
78
  IDEMPOTENCY_DISABLED_ENV: str = "POWERTOOLS_IDEMPOTENCY_DISABLED"
79
+
80
+ # Circuit breaker constants
81
+ CIRCUIT_BREAKER_DISABLED_ENV: str = "POWERTOOLS_CIRCUIT_BREAKER_DISABLED"
@@ -1,3 +1,3 @@
1
1
  """Exposes version constant to avoid circular dependencies."""
2
2
 
3
- VERSION = "3.30.0"
3
+ VERSION = "3.31.1"
@@ -0,0 +1,35 @@
1
+ """
2
+ Circuit Breaker utility for protecting unhealthy downstream dependencies.
3
+
4
+ !!! warning "Alpha / experimental"
5
+ This utility is published under the `_alpha` namespace while we collect
6
+ feedback. The public API may change in a backwards-incompatible way before it
7
+ is promoted to GA. Pin your version and follow the tracking discussion before
8
+ relying on it in production.
9
+ """
10
+
11
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.circuit_breaker import circuit_breaker
12
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig
13
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import (
14
+ CircuitBreakerConfigError,
15
+ CircuitBreakerError,
16
+ CircuitBreakerOpenError,
17
+ CircuitBreakerPersistenceError,
18
+ )
19
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import (
20
+ CircuitInfo,
21
+ CircuitState,
22
+ CircuitTransition,
23
+ )
24
+
25
+ __all__ = (
26
+ "circuit_breaker",
27
+ "CircuitBreakerConfig",
28
+ "CircuitInfo",
29
+ "CircuitState",
30
+ "CircuitTransition",
31
+ "CircuitBreakerError",
32
+ "CircuitBreakerOpenError",
33
+ "CircuitBreakerConfigError",
34
+ "CircuitBreakerPersistenceError",
35
+ )
@@ -0,0 +1,284 @@
1
+ """
2
+ Orchestrator for the Circuit Breaker utility.
3
+
4
+ :class:`CircuitBreakerHandler` owns the state machine and the per-environment failure
5
+ counter; the persistence layer owns the shared truth. This split keeps the healthy
6
+ path write-free: failures are counted locally and only persisted on a state transition.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import datetime
12
+ import logging
13
+ import os
14
+ import threading
15
+ import uuid
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerOpenError
19
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState, CircuitTransition
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Callable
23
+
24
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig
25
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import (
26
+ CircuitBreakerPersistenceLayer,
27
+ )
28
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitInfo
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ # Per-environment, per-circuit consecutive counters. Module-level so they survive across
33
+ # invocations within the same execution environment, the same way idempotency caches do.
34
+ _LOCAL_FAILURES: dict[str, int] = {}
35
+ _LOCAL_SUCCESSES: dict[str, int] = {}
36
+
37
+ # Tracks the last state this environment observed from the store, per circuit. Used to
38
+ # detect transitions back to CLOSED that happened externally (another env tripped and
39
+ # recovered), so stale local failure streaks can be invalidated.
40
+ _LAST_OBSERVED_STATE: dict[str, CircuitState] = {}
41
+
42
+ # Guards the three dicts above. Increments are read-modify-write and a threshold
43
+ # crossing must be observed by exactly one thread, so every access goes through this
44
+ # lock. Held only while mutating the dicts, never across persistence writes or user
45
+ # callbacks.
46
+ _COUNTERS_LOCK = threading.Lock()
47
+
48
+ # Identifier used to claim the half-open probe lock, unique per thread so the store's
49
+ # conditional election picks a single prober across threads as well as processes.
50
+ _PROBE_OWNER = threading.local()
51
+
52
+
53
+ def _probe_owner_id() -> str:
54
+ """
55
+ Return this thread's stable probe-owner identifier, minting it on first use.
56
+
57
+ A uuid in thread-local storage rather than ``threading.get_ident()``: the OS reuses
58
+ thread ids, and a recycled id would let an unrelated thread pass the owner check and
59
+ probe alongside the real owner. The pid check re-mints the id in forked children,
60
+ which inherit the forking thread's local storage.
61
+ """
62
+ pid = os.getpid()
63
+ if getattr(_PROBE_OWNER, "pid", None) != pid:
64
+ _PROBE_OWNER.id = f"{pid}#{uuid.uuid4().hex}"
65
+ _PROBE_OWNER.pid = pid
66
+ return _PROBE_OWNER.id
67
+
68
+
69
+ class CircuitBreakerHandler:
70
+ """
71
+ Drive a single protected call through the circuit breaker state machine.
72
+
73
+ A new handler is created per invocation by the decorator. It reads the shared state,
74
+ routes the call (run, short-circuit, or probe), and records the outcome.
75
+
76
+ Parameters
77
+ ----------
78
+ function : Callable
79
+ The protected function.
80
+ name : str
81
+ Circuit name.
82
+ config : CircuitBreakerConfig
83
+ Circuit configuration.
84
+ persistence_store : CircuitBreakerPersistenceLayer
85
+ Shared state store.
86
+ on_circuit_open : Callable | None
87
+ Callback invoked with the protected call's own ``*args``/``**kwargs`` plus a
88
+ trailing ``circuit`` keyword argument when the circuit is open. If ``None``, an
89
+ open circuit raises :class:`CircuitBreakerOpenError`.
90
+ function_args : tuple
91
+ Positional arguments the protected function was called with.
92
+ function_kwargs : dict
93
+ Keyword arguments the protected function was called with.
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ function: Callable,
99
+ name: str,
100
+ config: CircuitBreakerConfig,
101
+ persistence_store: CircuitBreakerPersistenceLayer,
102
+ on_circuit_open: Callable | None = None,
103
+ on_transition: Callable | None = None,
104
+ function_args: tuple | None = None,
105
+ function_kwargs: dict | None = None,
106
+ ):
107
+ self.function = function
108
+ self.name = name
109
+ self.config = config
110
+ self.on_circuit_open = on_circuit_open
111
+ self.on_transition = on_transition
112
+ self.fn_args = function_args or ()
113
+ self.fn_kwargs = function_kwargs or {}
114
+
115
+ persistence_store.configure(config=config, circuit_name=name)
116
+ self.persistence_store = persistence_store
117
+
118
+ def handle(self) -> Any:
119
+ """
120
+ Evaluate the circuit and route the call.
121
+
122
+ Returns
123
+ -------
124
+ Any
125
+ The protected function's result when the call runs, or the
126
+ ``on_circuit_open`` callback's return value when the circuit is open.
127
+
128
+ Raises
129
+ ------
130
+ CircuitBreakerOpenError
131
+ If the circuit is open and no callback is registered.
132
+ """
133
+ record = self.persistence_store.get_state(self.name)
134
+
135
+ if record.state == CircuitState.CLOSED:
136
+ # If we previously observed a non-CLOSED state and the circuit is now back to
137
+ # CLOSED, another environment completed the recovery cycle. Reset local counters
138
+ # so a stale partial failure streak doesn't immediately re-trip the circuit.
139
+ with _COUNTERS_LOCK:
140
+ prev = _LAST_OBSERVED_STATE.get(self.name)
141
+ if prev is not None and prev != CircuitState.CLOSED:
142
+ _LOCAL_FAILURES[self.name] = 0
143
+ _LAST_OBSERVED_STATE[self.name] = CircuitState.CLOSED
144
+ return self._call_closed()
145
+
146
+ if record.state == CircuitState.OPEN:
147
+ with _COUNTERS_LOCK:
148
+ _LAST_OBSERVED_STATE[self.name] = CircuitState.OPEN
149
+ # ``opened_at`` may legitimately be 0 (epoch); treat only None as missing.
150
+ opened_at = record.opened_at if record.opened_at is not None else self._now()
151
+ if self._now() >= opened_at + self.config.recovery_timeout:
152
+ # Recovery window elapsed: try to become the single prober.
153
+ if self.persistence_store.try_acquire_half_open(self.name, _probe_owner_id(), opened_at):
154
+ self._notify(CircuitState.OPEN, CircuitState.HALF_OPEN, opened_at=opened_at)
155
+ return self._call_probe()
156
+ return self._open_response(record.to_circuit_info())
157
+
158
+ # HALF_OPEN: only the thread that owns the probe lock runs.
159
+ with _COUNTERS_LOCK:
160
+ _LAST_OBSERVED_STATE[self.name] = CircuitState.HALF_OPEN
161
+ if record.half_open_owner == _probe_owner_id():
162
+ return self._call_probe()
163
+
164
+ # If the probe lease has expired (owner recycled mid-probe), take over.
165
+ if record.probe_lease_expiry is not None and self._now() >= record.probe_lease_expiry:
166
+ logger.debug("Circuit '%s' probe lease expired; attempting takeover.", self.name)
167
+ if self.persistence_store.try_acquire_half_open(self.name, _probe_owner_id(), record.opened_at or 0):
168
+ return self._call_probe()
169
+
170
+ return self._open_response(record.to_circuit_info())
171
+
172
+ def _call_closed(self) -> Any:
173
+ """Run the protected call while the circuit is closed, tracking failures."""
174
+ try:
175
+ result = self.function(*self.fn_args, **self.fn_kwargs)
176
+ except Exception as exc:
177
+ if not self.config.counts_as_failure(exc):
178
+ raise
179
+ # Increment and reset atomically so exactly one thread observes the threshold
180
+ # crossing; racing threads would otherwise lose increments (tripping late) or
181
+ # each persist the same transition.
182
+ with _COUNTERS_LOCK:
183
+ failures = _LOCAL_FAILURES.get(self.name, 0) + 1
184
+ tripped = failures >= self.config.failure_threshold
185
+ _LOCAL_FAILURES[self.name] = 0 if tripped else failures
186
+ if tripped:
187
+ logger.debug("Circuit '%s' tripping CLOSED to OPEN after %d failures.", self.name, failures)
188
+ opened_at = self._now()
189
+ self._safe_persist(
190
+ self.persistence_store.save_open,
191
+ self.name,
192
+ failure_count=failures,
193
+ opened_at=opened_at,
194
+ )
195
+ self._notify(CircuitState.CLOSED, CircuitState.OPEN, opened_at=opened_at)
196
+ raise
197
+ else:
198
+ with _COUNTERS_LOCK:
199
+ _LOCAL_FAILURES[self.name] = 0
200
+ return result
201
+
202
+ def _call_probe(self) -> Any:
203
+ """Run a probe during half-open, closing or reopening based on the outcome."""
204
+ try:
205
+ result = self.function(*self.fn_args, **self.fn_kwargs)
206
+ except Exception as exc:
207
+ if not self.config.counts_as_failure(exc):
208
+ raise
209
+ logger.debug("Circuit '%s' probe failed; reopening.", self.name)
210
+ opened_at = self._now()
211
+ self._safe_persist(self.persistence_store.save_reopen, self.name, opened_at=opened_at)
212
+ with _COUNTERS_LOCK:
213
+ _LOCAL_SUCCESSES[self.name] = 0
214
+ self._notify(CircuitState.HALF_OPEN, CircuitState.OPEN, opened_at=opened_at)
215
+ raise
216
+ else:
217
+ with _COUNTERS_LOCK:
218
+ successes = _LOCAL_SUCCESSES.get(self.name, 0) + 1
219
+ closed = successes >= self.config.success_threshold
220
+ _LOCAL_SUCCESSES[self.name] = 0 if closed else successes
221
+ if closed:
222
+ _LOCAL_FAILURES[self.name] = 0
223
+ if closed:
224
+ logger.debug("Circuit '%s' closing after %d probe successes.", self.name, successes)
225
+ self._safe_persist(self.persistence_store.save_closed, self.name)
226
+ self._notify(CircuitState.HALF_OPEN, CircuitState.CLOSED)
227
+ return result
228
+
229
+ def _safe_persist(self, fn: Callable, *args: Any, **kwargs: Any) -> None:
230
+ """
231
+ Call a persistence write, swallowing and logging failures.
232
+
233
+ State-transition writes must never mask the downstream's real result or replace
234
+ the downstream's real exception. This mirrors the fail-open read policy in the
235
+ persistence layer.
236
+ """
237
+ try:
238
+ fn(*args, **kwargs)
239
+ except Exception:
240
+ logger.warning(
241
+ "Circuit '%s': persistence write (%s) failed; the transition may be delayed but the "
242
+ "downstream result is preserved.",
243
+ self.name,
244
+ getattr(fn, "__name__", repr(fn)),
245
+ exc_info=True,
246
+ )
247
+
248
+ def _open_response(self, circuit: CircuitInfo) -> Any:
249
+ """Produce the response for an open circuit: callback result or raise."""
250
+ if self.on_circuit_open is not None:
251
+ # Forward the protected call's arguments unchanged: positional stay positional,
252
+ # keyword stay keyword. The circuit snapshot is passed as a keyword argument so
253
+ # it never collides with positionalized kwargs nor depends on dict ordering.
254
+ return self.on_circuit_open(*self.fn_args, **self.fn_kwargs, circuit=circuit)
255
+ raise CircuitBreakerOpenError(
256
+ f"Circuit '{self.name}' is open.",
257
+ circuit=circuit,
258
+ )
259
+
260
+ def _notify(self, from_state: CircuitState, to_state: CircuitState, opened_at: int | None = None) -> None:
261
+ """
262
+ Fire the ``on_transition`` hook for a state change.
263
+
264
+ Called only on real transitions, never on the hot path. Any exception the hook
265
+ raises is swallowed and logged: observability must never break the protected call.
266
+ """
267
+ if self.on_transition is None:
268
+ return
269
+ try:
270
+ self.on_transition(
271
+ CircuitTransition(
272
+ circuit_name=self.name,
273
+ from_state=from_state,
274
+ to_state=to_state,
275
+ opened_at=opened_at,
276
+ ),
277
+ )
278
+ except Exception:
279
+ logger.warning("on_transition hook for circuit '%s' raised; ignoring.", self.name, exc_info=True)
280
+
281
+ @staticmethod
282
+ def _now() -> int:
283
+ """Current unix timestamp in seconds."""
284
+ return int(datetime.datetime.now().timestamp())
@@ -0,0 +1,119 @@
1
+ """
2
+ Primary interface for the Circuit Breaker utility.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import functools
8
+ import logging
9
+ import os
10
+ import warnings
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from aws_lambda_powertools.shared import constants
14
+ from aws_lambda_powertools.shared.functions import strtobool
15
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.base import CircuitBreakerHandler
16
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.config import CircuitBreakerConfig
17
+ from aws_lambda_powertools.warnings import PowertoolsUserWarning
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Callable
21
+
22
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import (
23
+ CircuitBreakerPersistenceLayer,
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def circuit_breaker(
30
+ name: str,
31
+ persistence_store: CircuitBreakerPersistenceLayer,
32
+ on_circuit_open: Callable | None = None,
33
+ on_transition: Callable | None = None,
34
+ config: CircuitBreakerConfig | None = None,
35
+ ) -> Callable:
36
+ """
37
+ Protect a function that calls an unhealthy-prone downstream with a circuit breaker.
38
+
39
+ Wrap the function that makes the downstream call, not the whole Lambda handler, so a
40
+ tripped circuit reflects one dependency rather than unrelated handler logic.
41
+
42
+ When the circuit is open the protected function is not called. Instead, if an
43
+ ``on_circuit_open`` callback is registered it runs and its return value becomes the
44
+ call's result; otherwise :class:`CircuitBreakerOpenError` is raised.
45
+
46
+ Parameters
47
+ ----------
48
+ name : str
49
+ Unique circuit name. Each name is an independent circuit; a function calling
50
+ several backends should use one circuit per backend.
51
+ persistence_store : CircuitBreakerPersistenceLayer
52
+ Shared state store (for example ``CircuitBreakerDynamoDBPersistence``).
53
+ on_circuit_open : Callable | None
54
+ Called when the circuit is open, with the protected function's own arguments
55
+ (positional stay positional, keyword stay keyword) plus a trailing ``circuit``
56
+ keyword argument carrying a ``CircuitInfo``. Its return value becomes the call's
57
+ result. If ``None``, an open circuit raises ``CircuitBreakerOpenError``.
58
+ on_transition : Callable | None
59
+ Called with a single ``CircuitTransition`` argument whenever the circuit changes
60
+ state (open, probe, close, reopen). Fires only on transitions, never on the
61
+ per-invocation hot path, so it is a safe place to emit a CloudWatch metric. Any
62
+ exception it raises is swallowed and logged so observability never breaks the
63
+ protected call.
64
+ config : CircuitBreakerConfig | None
65
+ Tunables. Defaults to ``CircuitBreakerConfig()`` when omitted.
66
+
67
+ Returns
68
+ -------
69
+ Callable
70
+ The decorated function.
71
+
72
+ Example
73
+ -------
74
+ **Protect a payment backend, buffering rejected requests**
75
+
76
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha import circuit_breaker, CircuitInfo
77
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence import (
78
+ CircuitBreakerDynamoDBPersistence,
79
+ )
80
+
81
+ persistence = CircuitBreakerDynamoDBPersistence(table_name="CircuitBreakerState")
82
+
83
+ def buffer(order: dict, circuit: CircuitInfo):
84
+ sqs.send_message(QueueUrl=url, MessageBody=json.dumps(order))
85
+
86
+ @circuit_breaker(name="payment-backend", persistence_store=persistence, on_circuit_open=buffer)
87
+ def charge(order: dict) -> dict:
88
+ return payment_api.charge(order)
89
+ """
90
+ config = config or CircuitBreakerConfig()
91
+
92
+ def decorator(function: Callable) -> Callable:
93
+ @functools.wraps(function)
94
+ def wrapper(*args, **kwargs) -> Any:
95
+ # Skip the circuit entirely when disabled (development only).
96
+ if strtobool(os.getenv(constants.CIRCUIT_BREAKER_DISABLED_ENV, "false")):
97
+ warnings.warn(
98
+ message="Disabling the circuit breaker is intended for development environments only "
99
+ "and should not be used in production.",
100
+ category=PowertoolsUserWarning,
101
+ stacklevel=2,
102
+ )
103
+ return function(*args, **kwargs)
104
+
105
+ handler = CircuitBreakerHandler(
106
+ function=function,
107
+ name=name,
108
+ config=config,
109
+ persistence_store=persistence_store,
110
+ on_circuit_open=on_circuit_open,
111
+ on_transition=on_transition,
112
+ function_args=args,
113
+ function_kwargs=kwargs,
114
+ )
115
+ return handler.handle()
116
+
117
+ return wrapper
118
+
119
+ return decorator