aws-lambda-powertools 3.31.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.31.0 → aws_lambda_powertools-3.31.1}/PKG-INFO +2 -2
  2. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/appsync_events.py +1 -1
  3. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/dependant.py +8 -1
  4. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/version.py +1 -1
  5. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/base.py +59 -23
  6. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/config.py +64 -8
  7. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/base.py +70 -34
  8. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/dynamodb.py +46 -5
  9. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/record.py +2 -1
  10. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/event_bridge_event.py +1 -1
  11. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/event_source.py +24 -5
  12. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/dynamodb.py +2 -1
  13. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/pyproject.toml +2 -2
  14. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/setup.py +3 -3
  15. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/LICENSE +0 -0
  16. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/README.md +0 -0
  17. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/THIRD-PARTY-LICENSES +0 -0
  18. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/__init__.py +0 -0
  19. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/__init__.py +0 -0
  20. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/api_gateway.py +0 -0
  21. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/appsync.py +0 -0
  22. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/bedrock_agent.py +0 -0
  23. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/bedrock_agent_function.py +0 -0
  24. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/content_types.py +0 -0
  25. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/depends.py +0 -0
  26. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/__init__.py +0 -0
  27. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/_registry.py +0 -0
  28. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/base.py +0 -0
  29. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/exceptions.py +0 -0
  30. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/functions.py +0 -0
  31. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/router.py +0 -0
  32. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/events_appsync/types.py +0 -0
  33. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/exception_handling.py +0 -0
  34. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/exceptions.py +0 -0
  35. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/__init__.py +0 -0
  36. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/_registry.py +0 -0
  37. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/base.py +0 -0
  38. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/exceptions.py +0 -0
  39. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/graphql_appsync/router.py +0 -0
  40. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/http_resolver.py +0 -0
  41. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/lambda_function_url.py +0 -0
  42. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/__init__.py +0 -0
  43. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/async_utils.py +0 -0
  44. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/base.py +0 -0
  45. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/openapi_validation.py +0 -0
  46. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/middlewares/schema_validation.py +0 -0
  47. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/__init__.py +0 -0
  48. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/compat.py +0 -0
  49. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/config.py +0 -0
  50. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/constants.py +0 -0
  51. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/encoders.py +0 -0
  52. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/exceptions.py +0 -0
  53. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/merge.py +0 -0
  54. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/models.py +0 -0
  55. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/params.py +0 -0
  56. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/pydantic_loader.py +0 -0
  57. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/schema_generator.py +0 -0
  58. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/__init__.py +0 -0
  59. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/html.py +0 -0
  60. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/oauth2.py +0 -0
  61. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui-bundle.min.js +0 -0
  62. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/swagger_ui/swagger-ui.min.css +0 -0
  63. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/openapi/types.py +0 -0
  64. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/request.py +0 -0
  65. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/router.py +0 -0
  66. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/types.py +0 -0
  67. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/util.py +0 -0
  68. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/event_handler/vpc_lattice.py +0 -0
  69. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/exceptions/__init__.py +0 -0
  70. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/__init__.py +0 -0
  71. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/__init__.py +0 -0
  72. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/cache.py +0 -0
  73. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/config.py +0 -0
  74. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/functions.py +0 -0
  75. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/buffer/handler.py +0 -0
  76. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/constants.py +0 -0
  77. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/correlation_paths.py +0 -0
  78. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/exceptions.py +0 -0
  79. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/filters.py +0 -0
  80. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatter.py +0 -0
  81. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatters/__init__.py +0 -0
  82. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/formatters/datadog.py +0 -0
  83. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/lambda_context.py +0 -0
  84. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/logger.py +0 -0
  85. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/types.py +0 -0
  86. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/logging/utils.py +0 -0
  87. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/__init__.py +0 -0
  88. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/base.py +0 -0
  89. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/exceptions.py +0 -0
  90. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/functions.py +0 -0
  91. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/metric.py +0 -0
  92. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/metrics.py +0 -0
  93. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/__init__.py +0 -0
  94. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/base.py +0 -0
  95. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/__init__.py +0 -0
  96. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/cloudwatch.py +0 -0
  97. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/constants.py +0 -0
  98. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/exceptions.py +0 -0
  99. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/metric_properties.py +0 -0
  100. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cloudwatch_emf/types.py +0 -0
  101. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/cold_start.py +0 -0
  102. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/__init__.py +0 -0
  103. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/datadog.py +0 -0
  104. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/metrics.py +0 -0
  105. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/provider/datadog/warnings.py +0 -0
  106. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/metrics/types.py +0 -0
  107. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/__init__.py +0 -0
  108. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/exceptions.py +0 -0
  109. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/middleware_factory/factory.py +0 -0
  110. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/package_logger.py +0 -0
  111. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/py.typed +0 -0
  112. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/__init__.py +0 -0
  113. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/cache_dict.py +0 -0
  114. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/constants.py +0 -0
  115. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/cookies.py +0 -0
  116. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/dynamodb_deserializer.py +0 -0
  117. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/functions.py +0 -0
  118. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/headers_serializer.py +0 -0
  119. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/json_encoder.py +0 -0
  120. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/lazy_import.py +0 -0
  121. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/types.py +0 -0
  122. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/shared/user_agent.py +0 -0
  123. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/__init__.py +0 -0
  124. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/base.py +0 -0
  125. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/extensions.py +0 -0
  126. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/tracing/tracer.py +0 -0
  127. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/__init__.py +0 -0
  128. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/__init__.py +0 -0
  129. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/base.py +0 -0
  130. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/decorators.py +0 -0
  131. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/exceptions.py +0 -0
  132. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/sqs_fifo_partial_processor.py +0 -0
  133. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/batch/types.py +0 -0
  134. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/__init__.py +0 -0
  135. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/circuit_breaker.py +0 -0
  136. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/exceptions.py +0 -0
  137. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/persistence/__init__.py +0 -0
  138. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/circuit_breaker_alpha/states.py +0 -0
  139. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/__init__.py +0 -0
  140. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/active_mq_event.py +0 -0
  141. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/alb_event.py +0 -0
  142. {aws_lambda_powertools-3.31.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.31.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.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync/__init__.py +0 -0
  146. {aws_lambda_powertools-3.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync_authorizer_event.py +0 -0
  148. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/appsync_resolver_event.py +0 -0
  149. {aws_lambda_powertools-3.31.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.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/bedrock_agent_event.py +0 -0
  152. {aws_lambda_powertools-3.31.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.31.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.31.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.31.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.31.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.31.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.31.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.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/common.py +0 -0
  161. {aws_lambda_powertools-3.31.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.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/iot_registry_event.py +0 -0
  164. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kafka_event.py +0 -0
  165. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kinesis_firehose_event.py +0 -0
  166. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/kinesis_stream_event.py +0 -0
  167. {aws_lambda_powertools-3.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/rabbit_mq_event.py +0 -0
  169. {aws_lambda_powertools-3.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/s3_event.py +0 -0
  171. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/s3_object_event.py +0 -0
  172. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/secrets_manager_event.py +0 -0
  173. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/ses_event.py +0 -0
  174. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/shared_functions.py +0 -0
  175. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/sns_event.py +0 -0
  176. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/sqs_event.py +0 -0
  177. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/transfer_family_event.py +0 -0
  178. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_classes/vpc_lattice.py +0 -0
  179. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/__init__.py +0 -0
  180. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/base.py +0 -0
  181. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/constants.py +0 -0
  182. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/exceptions.py +0 -0
  183. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/__init__.py +0 -0
  184. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/base.py +0 -0
  185. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/data_masking/provider/kms/__init__.py +0 -0
  186. {aws_lambda_powertools-3.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/__init__.py +0 -0
  188. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/appconfig.py +0 -0
  189. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/base.py +0 -0
  190. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/comparators.py +0 -0
  191. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/constants.py +0 -0
  192. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/exceptions.py +0 -0
  193. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/feature_flags.py +0 -0
  194. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/schema.py +0 -0
  195. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/feature_flags/types.py +0 -0
  196. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/__init__.py +0 -0
  197. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/base.py +0 -0
  198. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/config.py +0 -0
  199. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/exceptions.py +0 -0
  200. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/hook.py +0 -0
  201. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/idempotency.py +0 -0
  202. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/__init__.py +0 -0
  203. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/base.py +0 -0
  204. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/cache.py +0 -0
  205. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/datarecord.py +0 -0
  206. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py +0 -0
  207. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/persistence/redis.py +0 -0
  208. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/__init__.py +0 -0
  209. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/base.py +0 -0
  210. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/custom_dict.py +0 -0
  211. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/dataclass.py +0 -0
  212. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/functions.py +0 -0
  213. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/no_op.py +0 -0
  214. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/idempotency/serialization/pydantic.py +0 -0
  215. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/jmespath_utils/__init__.py +0 -0
  216. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/jmespath_utils/envelopes.py +0 -0
  217. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/__init__.py +0 -0
  218. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/consumer_records.py +0 -0
  219. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/__init__.py +0 -0
  220. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/avro.py +0 -0
  221. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/base.py +0 -0
  222. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/default.py +0 -0
  223. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/deserializer.py +0 -0
  224. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/json.py +0 -0
  225. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/deserializer/protobuf.py +0 -0
  226. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/exceptions.py +0 -0
  227. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/kafka_consumer.py +0 -0
  228. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/schema_config.py +0 -0
  229. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/__init__.py +0 -0
  230. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/base.py +0 -0
  231. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/custom_dict.py +0 -0
  232. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/dataclass.py +0 -0
  233. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/pydantic.py +0 -0
  234. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/serialization.py +0 -0
  235. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/kafka/serialization/types.py +0 -0
  236. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/__init__.py +0 -0
  237. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/exceptions.py +0 -0
  238. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/metadata/lambda_metadata.py +0 -0
  239. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/__init__.py +0 -0
  240. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/appconfig.py +0 -0
  241. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/base.py +0 -0
  242. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/constants.py +0 -0
  243. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/dynamodb.py +0 -0
  244. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/exceptions.py +0 -0
  245. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/secrets.py +0 -0
  246. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/ssm.py +0 -0
  247. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parameters/types.py +0 -0
  248. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/__init__.py +0 -0
  249. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/__init__.py +0 -0
  250. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigw.py +0 -0
  251. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigw_websocket.py +0 -0
  252. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/apigwv2.py +0 -0
  253. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/base.py +0 -0
  254. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/bedrock_agent.py +0 -0
  255. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/cloudwatch.py +0 -0
  256. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/dynamodb.py +0 -0
  257. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/event_bridge.py +0 -0
  258. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kafka.py +0 -0
  259. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kinesis.py +0 -0
  260. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/kinesis_firehose.py +0 -0
  261. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/lambda_function_url.py +0 -0
  262. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/sns.py +0 -0
  263. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/sqs.py +0 -0
  264. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/vpc_lattice.py +0 -0
  265. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/envelopes/vpc_latticev2.py +0 -0
  266. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/exceptions.py +0 -0
  267. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/functions.py +0 -0
  268. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/__init__.py +0 -0
  269. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/alb.py +0 -0
  270. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigw.py +0 -0
  271. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigw_websocket.py +0 -0
  272. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/apigwv2.py +0 -0
  273. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/appsync.py +0 -0
  274. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/appsync_events.py +0 -0
  275. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/bedrock_agent.py +0 -0
  276. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cloudformation_custom_resource.py +0 -0
  277. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cloudwatch.py +0 -0
  278. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/cognito.py +0 -0
  279. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/event_bridge.py +0 -0
  280. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/iot_registry_events.py +0 -0
  281. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kafka.py +0 -0
  282. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis.py +0 -0
  283. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose.py +0 -0
  284. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/kinesis_firehose_sqs.py +0 -0
  285. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/lambda_function_url.py +0 -0
  286. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3.py +0 -0
  287. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_batch_operation.py +0 -0
  288. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_event_notification.py +0 -0
  289. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/s3_object_event.py +0 -0
  290. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/ses.py +0 -0
  291. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/sns.py +0 -0
  292. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/sqs.py +0 -0
  293. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/transfer_family.py +0 -0
  294. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/vpc_lattice.py +0 -0
  295. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/models/vpc_latticev2.py +0 -0
  296. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/parser.py +0 -0
  297. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/parser/types.py +0 -0
  298. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/serialization.py +0 -0
  299. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/__init__.py +0 -0
  300. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/_s3_seekable_io.py +0 -0
  301. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/compat.py +0 -0
  302. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/constants.py +0 -0
  303. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/s3_object.py +0 -0
  304. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/__init__.py +0 -0
  305. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/base.py +0 -0
  306. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/csv.py +0 -0
  307. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/gzip.py +0 -0
  308. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/transformations/zip.py +0 -0
  309. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/streaming/types.py +0 -0
  310. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/__init__.py +0 -0
  311. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_client_context.py +0 -0
  312. {aws_lambda_powertools-3.31.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.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_cognito_identity.py +0 -0
  314. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/typing/lambda_context.py +0 -0
  315. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/__init__.py +0 -0
  316. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/base.py +0 -0
  317. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/envelopes.py +0 -0
  318. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/exceptions.py +0 -0
  319. {aws_lambda_powertools-3.31.0 → aws_lambda_powertools-3.31.1}/aws_lambda_powertools/utilities/validation/validator.py +0 -0
  320. {aws_lambda_powertools-3.31.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.31.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"
@@ -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
@@ -1,3 +1,3 @@
1
1
  """Exposes version constant to avoid circular dependencies."""
2
2
 
3
- VERSION = "3.31.0"
3
+ VERSION = "3.31.1"
@@ -10,6 +10,8 @@ from __future__ import annotations
10
10
 
11
11
  import datetime
12
12
  import logging
13
+ import os
14
+ import threading
13
15
  import uuid
14
16
  from typing import TYPE_CHECKING, Any
15
17
 
@@ -37,8 +39,31 @@ _LOCAL_SUCCESSES: dict[str, int] = {}
37
39
  # recovered), so stale local failure streaks can be invalidated.
38
40
  _LAST_OBSERVED_STATE: dict[str, CircuitState] = {}
39
41
 
40
- # Stable per-environment identifier used to claim the half-open probe lock.
41
- _ENVIRONMENT_ID = uuid.uuid4().hex
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
42
67
 
43
68
 
44
69
  class CircuitBreakerHandler:
@@ -111,32 +136,35 @@ class CircuitBreakerHandler:
111
136
  # If we previously observed a non-CLOSED state and the circuit is now back to
112
137
  # CLOSED, another environment completed the recovery cycle. Reset local counters
113
138
  # so a stale partial failure streak doesn't immediately re-trip the circuit.
114
- prev = _LAST_OBSERVED_STATE.get(self.name)
115
- if prev is not None and prev != CircuitState.CLOSED:
116
- _LOCAL_FAILURES[self.name] = 0
117
- _LAST_OBSERVED_STATE[self.name] = CircuitState.CLOSED
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
118
144
  return self._call_closed()
119
145
 
120
146
  if record.state == CircuitState.OPEN:
121
- _LAST_OBSERVED_STATE[self.name] = CircuitState.OPEN
147
+ with _COUNTERS_LOCK:
148
+ _LAST_OBSERVED_STATE[self.name] = CircuitState.OPEN
122
149
  # ``opened_at`` may legitimately be 0 (epoch); treat only None as missing.
123
150
  opened_at = record.opened_at if record.opened_at is not None else self._now()
124
151
  if self._now() >= opened_at + self.config.recovery_timeout:
125
152
  # Recovery window elapsed: try to become the single prober.
126
- if self.persistence_store.try_acquire_half_open(self.name, _ENVIRONMENT_ID, opened_at):
153
+ if self.persistence_store.try_acquire_half_open(self.name, _probe_owner_id(), opened_at):
127
154
  self._notify(CircuitState.OPEN, CircuitState.HALF_OPEN, opened_at=opened_at)
128
155
  return self._call_probe()
129
156
  return self._open_response(record.to_circuit_info())
130
157
 
131
- # HALF_OPEN: only the environment that owns the probe lock runs.
132
- _LAST_OBSERVED_STATE[self.name] = CircuitState.HALF_OPEN
133
- if record.half_open_owner == _ENVIRONMENT_ID:
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():
134
162
  return self._call_probe()
135
163
 
136
164
  # If the probe lease has expired (owner recycled mid-probe), take over.
137
165
  if record.probe_lease_expiry is not None and self._now() >= record.probe_lease_expiry:
138
166
  logger.debug("Circuit '%s' probe lease expired; attempting takeover.", self.name)
139
- if self.persistence_store.try_acquire_half_open(self.name, _ENVIRONMENT_ID, record.opened_at or 0):
167
+ if self.persistence_store.try_acquire_half_open(self.name, _probe_owner_id(), record.opened_at or 0):
140
168
  return self._call_probe()
141
169
 
142
170
  return self._open_response(record.to_circuit_info())
@@ -148,9 +176,14 @@ class CircuitBreakerHandler:
148
176
  except Exception as exc:
149
177
  if not self.config.counts_as_failure(exc):
150
178
  raise
151
- failures = _LOCAL_FAILURES.get(self.name, 0) + 1
152
- _LOCAL_FAILURES[self.name] = failures
153
- if failures >= self.config.failure_threshold:
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:
154
187
  logger.debug("Circuit '%s' tripping CLOSED to OPEN after %d failures.", self.name, failures)
155
188
  opened_at = self._now()
156
189
  self._safe_persist(
@@ -159,11 +192,11 @@ class CircuitBreakerHandler:
159
192
  failure_count=failures,
160
193
  opened_at=opened_at,
161
194
  )
162
- _LOCAL_FAILURES[self.name] = 0
163
195
  self._notify(CircuitState.CLOSED, CircuitState.OPEN, opened_at=opened_at)
164
196
  raise
165
197
  else:
166
- _LOCAL_FAILURES[self.name] = 0
198
+ with _COUNTERS_LOCK:
199
+ _LOCAL_FAILURES[self.name] = 0
167
200
  return result
168
201
 
169
202
  def _call_probe(self) -> Any:
@@ -176,17 +209,20 @@ class CircuitBreakerHandler:
176
209
  logger.debug("Circuit '%s' probe failed; reopening.", self.name)
177
210
  opened_at = self._now()
178
211
  self._safe_persist(self.persistence_store.save_reopen, self.name, opened_at=opened_at)
179
- _LOCAL_SUCCESSES[self.name] = 0
212
+ with _COUNTERS_LOCK:
213
+ _LOCAL_SUCCESSES[self.name] = 0
180
214
  self._notify(CircuitState.HALF_OPEN, CircuitState.OPEN, opened_at=opened_at)
181
215
  raise
182
216
  else:
183
- successes = _LOCAL_SUCCESSES.get(self.name, 0) + 1
184
- _LOCAL_SUCCESSES[self.name] = successes
185
- if successes >= self.config.success_threshold:
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:
186
224
  logger.debug("Circuit '%s' closing after %d probe successes.", self.name, successes)
187
225
  self._safe_persist(self.persistence_store.save_closed, self.name)
188
- _LOCAL_SUCCESSES[self.name] = 0
189
- _LOCAL_FAILURES[self.name] = 0
190
226
  self._notify(CircuitState.HALF_OPEN, CircuitState.CLOSED)
191
227
  return result
192
228
 
@@ -4,8 +4,13 @@ Configuration for the Circuit Breaker utility.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ from typing import TYPE_CHECKING
8
+
7
9
  from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerConfigError
8
10
 
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Iterable
13
+
9
14
 
10
15
  class CircuitBreakerConfig:
11
16
  """
@@ -24,12 +29,14 @@ class CircuitBreakerConfig:
24
29
  success_threshold : int
25
30
  Number of *consecutive* probe successes required to close a half-open circuit.
26
31
  Defaults to 3.
27
- handled_exceptions : tuple[type[Exception], ...] | None
32
+ handled_exceptions : type[Exception] | Iterable[type[Exception]] | None
28
33
  Allowlist: only these exception types count as failures; anything else
29
- propagates without affecting the circuit. Mutually exclusive with
34
+ propagates without affecting the circuit. Accepts a single exception type or
35
+ an iterable of them (normalized to a tuple). Mutually exclusive with
30
36
  ``ignored_exceptions``. Defaults to ``None`` (treated as ``(Exception,)``).
31
- ignored_exceptions : tuple[type[Exception], ...] | None
32
- Denylist: every exception counts as a failure *except* these. Mutually
37
+ ignored_exceptions : type[Exception] | Iterable[type[Exception]] | None
38
+ Denylist: every exception counts as a failure *except* these. Accepts a single
39
+ exception type or an iterable of them (normalized to a tuple). Mutually
33
40
  exclusive with ``handled_exceptions``. Defaults to ``None``.
34
41
  local_cache_max_age : int
35
42
  Seconds a circuit's state is cached in the execution environment before a
@@ -38,8 +45,9 @@ class CircuitBreakerConfig:
38
45
  Raises
39
46
  ------
40
47
  CircuitBreakerConfigError
41
- If both ``handled_exceptions`` and ``ignored_exceptions`` are provided, or a
42
- numeric tunable is not a positive integer.
48
+ If both ``handled_exceptions`` and ``ignored_exceptions`` are provided, a
49
+ numeric tunable is not a positive integer, or an exception allowlist/denylist
50
+ is empty or contains a value that is not an exception type.
43
51
 
44
52
  Example
45
53
  -------
@@ -57,10 +65,16 @@ class CircuitBreakerConfig:
57
65
  failure_threshold: int = 5,
58
66
  recovery_timeout: int = 30,
59
67
  success_threshold: int = 3,
60
- handled_exceptions: tuple[type[Exception], ...] | None = None,
61
- ignored_exceptions: tuple[type[Exception], ...] | None = None,
68
+ handled_exceptions: type[Exception] | Iterable[type[Exception]] | None = None,
69
+ ignored_exceptions: type[Exception] | Iterable[type[Exception]] | None = None,
62
70
  local_cache_max_age: int = 5,
63
71
  ):
72
+ # Normalize first: a single exception type or any iterable becomes a tuple, and a
73
+ # bad value fails here (at construction) rather than as a cryptic isinstance
74
+ # TypeError later, the first time the circuit evaluates a failure.
75
+ handled_exceptions = self._normalize_exceptions(handled_exceptions, "handled_exceptions")
76
+ ignored_exceptions = self._normalize_exceptions(ignored_exceptions, "ignored_exceptions")
77
+
64
78
  self._validate(
65
79
  failure_threshold=failure_threshold,
66
80
  recovery_timeout=recovery_timeout,
@@ -105,6 +119,48 @@ class CircuitBreakerConfig:
105
119
  f"local_cache_max_age must be a non-negative integer, got {local_cache_max_age!r}.",
106
120
  )
107
121
 
122
+ @classmethod
123
+ def _normalize_exceptions(
124
+ cls,
125
+ value: type[Exception] | Iterable[type[Exception]] | None,
126
+ field: str,
127
+ ) -> tuple[type[Exception], ...] | None:
128
+ """Coerce a single exception type or an iterable of them into a validated, non-empty tuple.
129
+
130
+ Runs at construction so a bad value fails immediately with a clear error, rather
131
+ than as a cryptic ``isinstance`` ``TypeError`` from ``counts_as_failure`` the
132
+ first time the circuit evaluates a failure (i.e. only once the dependency is
133
+ already unhealthy).
134
+ """
135
+ if value is None:
136
+ return None
137
+
138
+ invalid = f"{field} must be an exception type or an iterable of exception types, got {value!r}."
139
+ # A str is iterable; reject it rather than iterate it as a sequence of characters.
140
+ if isinstance(value, str):
141
+ raise CircuitBreakerConfigError(invalid)
142
+
143
+ if isinstance(value, type):
144
+ # ty (unlike mypy) does not narrow the union here, so it needs the ignore.
145
+ exceptions: tuple[type[Exception], ...] = (value,) # ty: ignore[invalid-assignment]
146
+ else:
147
+ try:
148
+ exceptions = tuple(value)
149
+ except TypeError:
150
+ raise CircuitBreakerConfigError(invalid) from None
151
+
152
+ cls._validate_exception_types(exceptions, field)
153
+ return exceptions
154
+
155
+ @staticmethod
156
+ def _validate_exception_types(exceptions: tuple[type[Exception], ...], field: str) -> None:
157
+ """Require a non-empty tuple whose every element is an exception type."""
158
+ if not exceptions:
159
+ raise CircuitBreakerConfigError(f"{field} must contain at least one exception type.")
160
+ for exception in exceptions:
161
+ if not (isinstance(exception, type) and issubclass(exception, Exception)):
162
+ raise CircuitBreakerConfigError(f"{field} must contain only exception types, got {exception!r}.")
163
+
108
164
  def counts_as_failure(self, exception: Exception) -> bool:
109
165
  """
110
166
  Decide whether an exception raised by the protected call counts as a circuit failure.
@@ -10,8 +10,9 @@ from __future__ import annotations
10
10
 
11
11
  import datetime
12
12
  import logging
13
+ import threading
13
14
  from abc import ABC, abstractmethod
14
- from typing import TYPE_CHECKING
15
+ from typing import TYPE_CHECKING, NamedTuple
15
16
 
16
17
  from aws_lambda_powertools.shared.cache_dict import LRUDict
17
18
  from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord
@@ -32,6 +33,17 @@ LOCAL_CACHE_MAX_ITEMS = 1024
32
33
  PERSISTED_STATE_TTL_BUFFER = 3600
33
34
 
34
35
 
36
+ class _CircuitSettings(NamedTuple):
37
+ """Per-circuit tunables the layer captures from :meth:`configure`."""
38
+
39
+ local_cache_max_age: int
40
+ recovery_timeout: int
41
+
42
+
43
+ # Fallback for direct layer use before configure() has run; mirrors CircuitBreakerConfig defaults.
44
+ _DEFAULT_SETTINGS = _CircuitSettings(local_cache_max_age=5, recovery_timeout=30)
45
+
46
+
35
47
  class CircuitBreakerExistingLockError(Exception):
36
48
  """Internal signal that a conditional half-open probe write lost the race."""
37
49
 
@@ -50,19 +62,26 @@ class CircuitBreakerPersistenceLayer(ABC):
50
62
 
51
63
  def __init__(self) -> None:
52
64
  """Initialize defaults; real configuration happens in :meth:`configure`."""
53
- self.circuit_name: str = ""
54
- self.local_cache_max_age: int = 5
55
- self.recovery_timeout: int = 30
65
+ # Per-circuit tunables, keyed by circuit name. One persistence instance is shared
66
+ # by every circuit (and thread) using the same store, so these must never live in
67
+ # plain instance attributes: circuits with different configs would stamp each
68
+ # other's TTL and probe lease. A plain dict, not an LRUDict: evicting a live
69
+ # circuit's settings would silently swap in the defaults (wrong lease and TTL),
70
+ # whereas evicting a cache entry below only costs a store re-read.
71
+ self._settings: dict[str, _CircuitSettings] = {}
56
72
  # Maps circuit name -> the unix timestamp the locally cached record goes stale.
57
73
  # Kept separate from the record's durable ``expiry_timestamp`` (the store TTL) so
58
74
  # the short in-memory freshness window is never mistaken for the long store TTL.
59
75
  self._cache: LRUDict = LRUDict(max_items=LOCAL_CACHE_MAX_ITEMS)
76
+ # One lock for both maps: LRUDict reorders entries even on reads, so unguarded
77
+ # concurrent access can corrupt it or raise.
78
+ self._lock = threading.Lock()
60
79
 
61
80
  def configure(self, config: CircuitBreakerConfig, circuit_name: str) -> None:
62
81
  """
63
- Bind the layer to a circuit and its configuration.
82
+ Bind a circuit's configuration to the layer.
64
83
 
65
- Called once per invocation by the handler; the assignments are cheap and the
84
+ Called once per invocation by the handler; the assignment is cheap and the
66
85
  same persistence instance is reused across invocations within an environment.
67
86
 
68
87
  Parameters
@@ -70,43 +89,59 @@ class CircuitBreakerPersistenceLayer(ABC):
70
89
  config : CircuitBreakerConfig
71
90
  Configuration providing the local cache TTL and recovery timeout.
72
91
  circuit_name : str
73
- The circuit this layer instance serves.
92
+ The circuit these settings apply to.
74
93
  """
75
- self.circuit_name = circuit_name
76
- self.local_cache_max_age = config.local_cache_max_age
77
- self.recovery_timeout = config.recovery_timeout
94
+ with self._lock:
95
+ self._settings[circuit_name] = _CircuitSettings(
96
+ local_cache_max_age=config.local_cache_max_age,
97
+ recovery_timeout=config.recovery_timeout,
98
+ )
99
+
100
+ def _settings_for(self, name: str) -> _CircuitSettings:
101
+ """Return a circuit's configured settings, or the defaults if never configured."""
102
+ with self._lock:
103
+ return self._settings.get(name, _DEFAULT_SETTINGS)
78
104
 
79
105
  # ------------------------------------------------------------------ cache
80
106
 
81
107
  def _cache_key(self, name: str) -> str:
82
108
  return name
83
109
 
84
- def _durable_ttl(self) -> int:
110
+ def _durable_ttl(self, name: str) -> int:
85
111
  """
86
112
  Compute the store TTL stamped on a persisted record.
87
113
 
88
114
  Sized to outlive a full recovery window so a live circuit is never reaped
89
115
  mid-cycle, while an abandoned circuit (no further writes) self-cleans soon after.
90
116
  """
91
- return int(datetime.datetime.now().timestamp()) + self.recovery_timeout + PERSISTED_STATE_TTL_BUFFER
117
+ now = int(datetime.datetime.now().timestamp())
118
+ return now + self._settings_for(name).recovery_timeout + PERSISTED_STATE_TTL_BUFFER
92
119
 
93
120
  def _save_to_cache(self, record: CircuitStateRecord) -> None:
94
121
  """Cache a record locally with a short in-memory freshness window."""
95
- local_expiry = int(datetime.datetime.now().timestamp()) + self.local_cache_max_age
96
- self._cache[self._cache_key(record.name)] = (local_expiry, record)
122
+ local_expiry = int(datetime.datetime.now().timestamp()) + self._settings_for(record.name).local_cache_max_age
123
+ with self._lock:
124
+ self._cache[self._cache_key(record.name)] = (local_expiry, record)
97
125
 
98
126
  def _retrieve_from_cache(self, name: str) -> CircuitStateRecord | None:
99
127
  """Return a cached record if present and still within its local freshness window."""
100
- cached = self._cache.get(self._cache_key(name))
101
- if cached is None:
102
- return None
103
-
104
- local_expiry, record = cached
105
- if int(datetime.datetime.now().timestamp()) >= local_expiry:
106
- del self._cache[self._cache_key(name)]
107
- return None
108
-
109
- return record
128
+ with self._lock:
129
+ cached = self._cache.get(self._cache_key(name))
130
+ if cached is None:
131
+ return None
132
+
133
+ local_expiry, record = cached
134
+ if int(datetime.datetime.now().timestamp()) >= local_expiry:
135
+ # Guarded del, not pop: on Python 3.10 OrderedDict.pop re-enters the
136
+ # subclass __getitem__ after detaching the node, so LRUDict.pop raises
137
+ # KeyError for a *present* key and corrupts the dict (fixed in 3.11).
138
+ try:
139
+ del self._cache[self._cache_key(name)]
140
+ except KeyError:
141
+ pass
142
+ return None
143
+
144
+ return record
110
145
 
111
146
  # ------------------------------------------------------------- public API
112
147
 
@@ -175,19 +210,19 @@ class CircuitBreakerPersistenceLayer(ABC):
175
210
  state=CircuitState.OPEN,
176
211
  failure_count=failure_count,
177
212
  opened_at=opened_at,
178
- expiry_timestamp=self._durable_ttl(),
213
+ expiry_timestamp=self._durable_ttl(name),
179
214
  )
180
215
  self._put_record(record)
181
216
  self._save_to_cache(record)
182
217
 
183
218
  def try_acquire_half_open(self, name: str, owner: str, opened_at: int) -> bool:
184
219
  """
185
- Atomically elect a single environment to run the half-open probe.
220
+ Atomically elect a single worker to run the half-open probe.
186
221
 
187
222
  The conditional write succeeds only when the circuit is OPEN with no existing
188
223
  lock owner AND the ``opened_at`` matches what the caller observed (guards against
189
224
  stale eventually-consistent reads). A lease expiry is stamped so that if the
190
- winning environment is recycled before completing the probe, others can take over
225
+ winning worker is recycled before completing the probe, others can take over
191
226
  once the lease lapses.
192
227
 
193
228
  Parameters
@@ -195,25 +230,26 @@ class CircuitBreakerPersistenceLayer(ABC):
195
230
  name : str
196
231
  Circuit name.
197
232
  owner : str
198
- Identifier of the environment attempting the probe.
233
+ Identifier of the worker (one thread in one execution environment)
234
+ attempting the probe.
199
235
  opened_at : int
200
236
  The ``opened_at`` the caller observed, kept stable across the transition.
201
237
 
202
238
  Returns
203
239
  -------
204
240
  bool
205
- ``True`` if this environment won the probe lock, ``False`` if another
206
- environment already holds it.
241
+ ``True`` if this worker won the probe lock, ``False`` if another
242
+ worker already holds it.
207
243
  """
208
244
  # Lease = recovery_timeout gives the probe a full cycle to complete.
209
- probe_lease_expiry = int(datetime.datetime.now().timestamp()) + self.recovery_timeout
245
+ probe_lease_expiry = int(datetime.datetime.now().timestamp()) + self._settings_for(name).recovery_timeout
210
246
  record = CircuitStateRecord(
211
247
  name=name,
212
248
  state=CircuitState.HALF_OPEN,
213
249
  opened_at=opened_at,
214
250
  half_open_owner=owner,
215
251
  probe_lease_expiry=probe_lease_expiry,
216
- expiry_timestamp=self._durable_ttl(),
252
+ expiry_timestamp=self._durable_ttl(name),
217
253
  )
218
254
  try:
219
255
  self._put_record(record, condition="half_open", expected_opened_at=opened_at)
@@ -228,7 +264,7 @@ class CircuitBreakerPersistenceLayer(ABC):
228
264
  name=name,
229
265
  state=CircuitState.CLOSED,
230
266
  failure_count=0,
231
- expiry_timestamp=self._durable_ttl(),
267
+ expiry_timestamp=self._durable_ttl(name),
232
268
  )
233
269
  self._update_record(record)
234
270
  self._save_to_cache(record)
@@ -245,7 +281,7 @@ class CircuitBreakerPersistenceLayer(ABC):
245
281
  name=name,
246
282
  state=CircuitState.OPEN,
247
283
  opened_at=opened_at,
248
- expiry_timestamp=self._durable_ttl(),
284
+ expiry_timestamp=self._durable_ttl(name),
249
285
  )
250
286
  self._update_record(record)
251
287
  self._save_to_cache(record)
@@ -6,19 +6,23 @@ from __future__ import annotations
6
6
 
7
7
  import datetime
8
8
  import logging
9
+ import os
10
+ import warnings
9
11
  from typing import TYPE_CHECKING
10
12
 
11
13
  import boto3
12
14
  from boto3.dynamodb.types import TypeDeserializer
13
15
  from botocore.exceptions import ClientError
14
16
 
15
- from aws_lambda_powertools.shared import user_agent
17
+ from aws_lambda_powertools.shared import constants, user_agent
18
+ from aws_lambda_powertools.utilities.circuit_breaker_alpha.exceptions import CircuitBreakerConfigError
16
19
  from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.base import (
17
20
  CircuitBreakerExistingLockError,
18
21
  CircuitBreakerPersistenceLayer,
19
22
  )
20
23
  from aws_lambda_powertools.utilities.circuit_breaker_alpha.persistence.record import CircuitStateRecord
21
24
  from aws_lambda_powertools.utilities.circuit_breaker_alpha.states import CircuitState
25
+ from aws_lambda_powertools.warnings import PowertoolsUserWarning
22
26
 
23
27
  if TYPE_CHECKING:
24
28
  from botocore.config import Config
@@ -41,6 +45,13 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
41
45
  Name of the DynamoDB table that stores circuit state.
42
46
  key_attr : str
43
47
  Partition key attribute holding the circuit name. Defaults to ``"id"``.
48
+ static_pk_value : str, optional
49
+ Partition key value used when ``sort_key_attr`` is set, so the circuit name
50
+ moves to the sort key. Defaults to ``"circuit_breaker#<function-name>"``.
51
+ sort_key_attr : str, optional
52
+ Sort key attribute holding the circuit name. When set, the table is treated as
53
+ composite: ``static_pk_value`` is written to the partition key and the circuit
54
+ name to the sort key. Omit it for the default partition-key-only behavior.
44
55
  state_attr : str
45
56
  Attribute holding the circuit state. Defaults to ``"state"``.
46
57
  failure_count_attr : str
@@ -75,6 +86,8 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
75
86
  self,
76
87
  table_name: str,
77
88
  key_attr: str = "id",
89
+ static_pk_value: str | None = None,
90
+ sort_key_attr: str | None = None,
78
91
  state_attr: str = "state",
79
92
  failure_count_attr: str = "failure_count",
80
93
  opened_at_attr: str = "opened_at",
@@ -92,8 +105,25 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
92
105
 
93
106
  user_agent.register_feature_to_client(client=self.client, feature="circuit_breaker")
94
107
 
108
+ if sort_key_attr == key_attr:
109
+ raise CircuitBreakerConfigError(
110
+ f"key_attr [{key_attr}] and sort_key_attr [{sort_key_attr}] cannot be the same!",
111
+ )
112
+
113
+ if static_pk_value is not None and sort_key_attr is None:
114
+ warnings.warn(
115
+ "static_pk_value is ignored unless sort_key_attr is also set.",
116
+ category=PowertoolsUserWarning,
117
+ stacklevel=2,
118
+ )
119
+
120
+ if static_pk_value is None:
121
+ static_pk_value = f"circuit_breaker#{os.getenv(constants.LAMBDA_FUNCTION_NAME_ENV, '')}"
122
+
95
123
  self.table_name = table_name
96
124
  self.key_attr = key_attr
125
+ self.static_pk_value = static_pk_value
126
+ self.sort_key_attr = sort_key_attr
97
127
  self.state_attr = state_attr
98
128
  self.failure_count_attr = failure_count_attr
99
129
  self.opened_at_attr = opened_at_attr
@@ -111,7 +141,7 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
111
141
  opened_at = data.get(self.opened_at_attr)
112
142
  probe_lease_expiry = data.get(self.probe_lease_expiry_attr)
113
143
  return CircuitStateRecord(
114
- name=data[self.key_attr],
144
+ name=data[self.sort_key_attr] if self.sort_key_attr else data[self.key_attr],
115
145
  state=CircuitState(data[self.state_attr]),
116
146
  failure_count=int(data.get(self.failure_count_attr, 0)),
117
147
  opened_at=int(opened_at) if opened_at is not None else None,
@@ -130,10 +160,21 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
130
160
  """Wrap a value as a DynamoDB string attribute."""
131
161
  return {"S": value}
132
162
 
163
+ def _get_key(self, name: str) -> dict:
164
+ """Build the simple or composite primary key for a circuit.
165
+
166
+ When ``sort_key_attr`` is set the key is composite: ``static_pk_value`` in the
167
+ partition and the circuit name in the sort key. Otherwise the name is the
168
+ partition key.
169
+ """
170
+ if self.sort_key_attr:
171
+ return {self.key_attr: self._s(self.static_pk_value), self.sort_key_attr: self._s(name)}
172
+ return {self.key_attr: self._s(name)}
173
+
133
174
  def _record_to_item(self, record: CircuitStateRecord) -> dict:
134
175
  """Translate a :class:`CircuitStateRecord` into a DynamoDB item."""
135
176
  item: dict = {
136
- self.key_attr: self._s(record.name),
177
+ **self._get_key(record.name),
137
178
  self.state_attr: self._s(str(record.state)),
138
179
  self.failure_count_attr: self._n(record.failure_count),
139
180
  }
@@ -152,7 +193,7 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
152
193
  # and halves the read cost on the hot path.
153
194
  response = self.client.get_item(
154
195
  TableName=self.table_name,
155
- Key={self.key_attr: self._s(name)},
196
+ Key=self._get_key(name),
156
197
  ConsistentRead=False,
157
198
  )
158
199
  item = response.get("Item")
@@ -242,7 +283,7 @@ class CircuitBreakerDynamoDBPersistence(CircuitBreakerPersistenceLayer):
242
283
 
243
284
  return {
244
285
  "TableName": self.table_name,
245
- "Key": {self.key_attr: self._s(record.name)},
286
+ "Key": self._get_key(record.name),
246
287
  "UpdateExpression": update_expression,
247
288
  "ExpressionAttributeNames": expression_attr_names,
248
289
  "ExpressionAttributeValues": expression_attr_values,