modmex-lambda 0.4.1__tar.gz → 0.5.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 (229) hide show
  1. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/PKG-INFO +70 -1
  2. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/README.md +69 -0
  3. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/__init__.py +2 -0
  4. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/gateway_response.py +7 -2
  5. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/logging.py +55 -26
  6. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/base_flavor.py +3 -3
  7. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/publisher.py +1 -1
  8. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/runner.py +20 -4
  9. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/eventbridge.py +6 -7
  10. modmex_lambda-0.5.1/modmex_lambda/tracing.py +189 -0
  11. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/pyproject.toml +1 -1
  12. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_api_gateway.py +33 -0
  13. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_dependencies.py +113 -1
  14. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_gateway_response.py +25 -0
  15. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/filters/test_event_type.py +9 -0
  16. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_base_flavor.py +7 -0
  17. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_collect.py +38 -0
  18. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/test_runner.py +34 -0
  19. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/test_runner_pipeline.py +1 -11
  20. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/faults.py +9 -1
  21. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_uow.py +20 -0
  22. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/test_lazy_imports.py +25 -0
  23. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/test_logging.py +98 -0
  24. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/test_parser_event_sources.py +26 -0
  25. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/test_reexports.py +2 -0
  26. modmex_lambda-0.5.1/tests/test_tracing.py +167 -0
  27. modmex_lambda-0.4.1/modmex_lambda/stream/utils/opt.py +0 -15
  28. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/.github/workflows/ci.yml +0 -0
  29. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/.github/workflows/release.yml +0 -0
  30. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/.gitignore +0 -0
  31. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/LICENSE +0 -0
  32. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/__init__.py +0 -0
  33. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/cloudwatch.py +0 -0
  34. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/dynamodb.py +0 -0
  35. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/eventbridge.py +0 -0
  36. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/icloudwatch.py +0 -0
  37. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/idynamodb.py +0 -0
  38. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/ieventbridge.py +0 -0
  39. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/ilambda.py +0 -0
  40. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/is3.py +0 -0
  41. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/isns.py +0 -0
  42. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/isqs.py +0 -0
  43. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/lambda_.py +0 -0
  44. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/module.py +0 -0
  45. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/s3.py +0 -0
  46. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/sns.py +0 -0
  47. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/connectors/sqs.py +0 -0
  48. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/__init__.py +0 -0
  49. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/api_gateway_authorizer_event.py +0 -0
  50. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/api_gateway_proxy_event.py +0 -0
  51. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/api_gateway_websocket_event.py +0 -0
  52. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/cognito_user_pool_event.py +0 -0
  53. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/data_classes/common.py +0 -0
  54. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/dependencies.py +0 -0
  55. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/__init__.py +0 -0
  56. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/api_gateway.py +0 -0
  57. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/constants.py +0 -0
  58. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/content_types.py +0 -0
  59. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/cors.py +0 -0
  60. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/__init__.py +0 -0
  61. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/compat.py +0 -0
  62. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/dependant.py +0 -0
  63. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/dependency_middleware.py +0 -0
  64. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/depends.py +0 -0
  65. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/params.py +0 -0
  66. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/dependencies/types.py +0 -0
  67. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/exception_handler.py +0 -0
  68. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/exceptions.py +0 -0
  69. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/middlewares.py +0 -0
  70. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/params.py +0 -0
  71. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/request.py +0 -0
  72. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/response.py +0 -0
  73. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/routing.py +0 -0
  74. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/routing_fallbacks.py +0 -0
  75. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_handler/types.py +0 -0
  76. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/event_sources.py +0 -0
  77. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/exceptions.py +0 -0
  78. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/params.py +0 -0
  79. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/parser.py +0 -0
  80. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/request.py +0 -0
  81. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/resolver.py +0 -0
  82. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/response.py +0 -0
  83. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/routing.py +0 -0
  84. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/shared/__init__.py +0 -0
  85. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/shared/cookies.py +0 -0
  86. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/shared/headers_serializer.py +0 -0
  87. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/shared/json_encoder.py +0 -0
  88. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/shared/types.py +0 -0
  89. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/__init__.py +0 -0
  90. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/__init__.py +0 -0
  91. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/dynamodb.py +0 -0
  92. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/kinesis.py +0 -0
  93. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/s3.py +0 -0
  94. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/sns.py +0 -0
  95. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/events/sqs.py +0 -0
  96. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/filters/__init__.py +0 -0
  97. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/filters/content.py +0 -0
  98. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/filters/event_type.py +0 -0
  99. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/filters/latch.py +0 -0
  100. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/filters/skip.py +0 -0
  101. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/__init__.py +0 -0
  102. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/cdc.py +0 -0
  103. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/collect.py +0 -0
  104. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/correlate.py +0 -0
  105. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/evaluate.py +0 -0
  106. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/expired.py +0 -0
  107. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/iflavor.py +0 -0
  108. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/job.py +0 -0
  109. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/materialize.py +0 -0
  110. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/s3.py +0 -0
  111. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/sns.py +0 -0
  112. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/task.py +0 -0
  113. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/flavors/update.py +0 -0
  114. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/irules_registry.py +0 -0
  115. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/__init__.py +0 -0
  116. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/cloudwatch.py +0 -0
  117. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/dynamodb.py +0 -0
  118. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/ioperator.py +0 -0
  119. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/lambda_.py +0 -0
  120. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/s3.py +0 -0
  121. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/sns.py +0 -0
  122. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/operators/sqs.py +0 -0
  123. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/rules_registry.py +0 -0
  124. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/__init__.py +0 -0
  125. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/base.py +0 -0
  126. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/dynamodb.py +0 -0
  127. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/kinesis.py +0 -0
  128. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/s3.py +0 -0
  129. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/sns.py +0 -0
  130. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/sources/sqs.py +0 -0
  131. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/__init__.py +0 -0
  132. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/apigateway.py +0 -0
  133. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/aws.py +0 -0
  134. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/batch.py +0 -0
  135. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/cloudwatch.py +0 -0
  136. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/concurrency.py +0 -0
  137. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/contracts.py +0 -0
  138. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/data_classes/__init__.py +0 -0
  139. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/data_classes/dynamodb.py +0 -0
  140. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/decorators.py +0 -0
  141. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/dynamodb.py +0 -0
  142. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/faults.py +0 -0
  143. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/filters.py +0 -0
  144. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/json_encoder.py +0 -0
  145. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/lambda_.py +0 -0
  146. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/operators.py +0 -0
  147. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/pluralize.py +0 -0
  148. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/print.py +0 -0
  149. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/retry.py +0 -0
  150. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/s3.py +0 -0
  151. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/sns.py +0 -0
  152. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/split.py +0 -0
  153. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/sqs.py +0 -0
  154. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/tags.py +0 -0
  155. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/time.py +0 -0
  156. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/stream/utils/uow.py +0 -0
  157. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/modmex_lambda/validation.py +0 -0
  158. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/poetry.lock +0 -0
  159. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/__init__.py +0 -0
  160. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/conftest.py +0 -0
  161. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/__init__.py +0 -0
  162. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/conftest.py +0 -0
  163. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_cloudwatch.py +0 -0
  164. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_dynamodb.py +0 -0
  165. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_lazy_clients.py +0 -0
  166. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_s3.py +0 -0
  167. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_simple_connectors.py +0 -0
  168. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_sns.py +0 -0
  169. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/connectors/test_sqs.py +0 -0
  170. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/data_classes/test_api_gateway_proxy_event.py +0 -0
  171. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/data_classes/test_cognito_user_pool_event.py +0 -0
  172. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/data_classes/test_common.py +0 -0
  173. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/__init__.py +0 -0
  174. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_cors.py +0 -0
  175. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_exception_handler.py +0 -0
  176. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_request.py +0 -0
  177. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_response.py +0 -0
  178. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/event_handler/test_routing.py +0 -0
  179. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/shared/test_cookies_headers.py +0 -0
  180. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/shared/test_json_encoder.py +0 -0
  181. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/__init__.py +0 -0
  182. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/conftest.py +0 -0
  183. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/events/test_dynamodb.py +0 -0
  184. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/events/test_kinesis.py +0 -0
  185. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/events/test_s3.py +0 -0
  186. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/events/test_sns.py +0 -0
  187. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/events/test_sqs.py +0 -0
  188. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/filters/__init__.py +0 -0
  189. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/filters/test_content.py +0 -0
  190. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/filters/test_latch.py +0 -0
  191. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/filters/test_skip.py +0 -0
  192. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/__init__.py +0 -0
  193. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/source_events.py +0 -0
  194. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_cdc.py +0 -0
  195. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_correlate.py +0 -0
  196. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_evaluate.py +0 -0
  197. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_expired.py +0 -0
  198. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_job.py +0 -0
  199. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_materialize.py +0 -0
  200. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_s3.py +0 -0
  201. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_sns.py +0 -0
  202. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_task.py +0 -0
  203. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/flavors/test_update.py +0 -0
  204. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/test_dependency_resolver.py +0 -0
  205. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/test_rules_registry.py +0 -0
  206. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/test_sources.py +0 -0
  207. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/__init__.py +0 -0
  208. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_apigateway.py +0 -0
  209. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_aws.py +0 -0
  210. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_batch.py +0 -0
  211. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_cloudwatch.py +0 -0
  212. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_concurrency.py +0 -0
  213. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_decorators.py +0 -0
  214. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_dynamodb.py +0 -0
  215. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_eventbridge.py +0 -0
  216. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_filters.py +0 -0
  217. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_json_encoder.py +0 -0
  218. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_lambda.py +0 -0
  219. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_operators.py +0 -0
  220. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_pluralize.py +0 -0
  221. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_print.py +0 -0
  222. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_retry.py +0 -0
  223. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_s3.py +0 -0
  224. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_sns.py +0 -0
  225. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_split.py +0 -0
  226. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_sqs.py +0 -0
  227. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_tags.py +0 -0
  228. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/stream/utils/test_time.py +0 -0
  229. {modmex_lambda-0.4.1 → modmex_lambda-0.5.1}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modmex-lambda
3
- Version: 0.4.1
3
+ Version: 0.5.1
4
4
  Summary: Ultra-lightweight AWS Lambda utilities for API Gateway routing and event handling.
5
5
  Author: Modmex
6
6
  License: MIT
@@ -607,6 +607,75 @@ The logger emits structured JSON, reads `LOG_LEVEL`, uses `SERVICE_NAME` or
607
607
  `AWS_LAMBDA_FUNCTION_NAME` when no service is passed, and can extract Lambda
608
608
  request IDs and API Gateway correlation IDs.
609
609
 
610
+ ## Tracing
611
+
612
+ Tracing is optional and lazy. The core package does not require
613
+ `opentelemetry-api`, does not initialize an SDK/exporter, and does not import
614
+ OpenTelemetry during `import modmex_lambda`.
615
+
616
+ `Tracer` uses OpenTelemetry when an OpenTelemetry runtime is available. For
617
+ Lambda, the recommended runtime is the AWS Distro for OpenTelemetry (ADOT)
618
+ Lambda layer.
619
+
620
+ The example below uses Serverless Framework. If you use another IaC tool, the
621
+ same pieces are required: enable Lambda tracing, attach the ADOT Python layer
622
+ for the AWS region where the function runs, and configure the OpenTelemetry
623
+ environment variables.
624
+
625
+ ```yaml
626
+ provider:
627
+ name: aws
628
+ runtime: python3.12
629
+ region: mx-central-1
630
+ tracing:
631
+ lambda: true
632
+
633
+ functions:
634
+ api:
635
+ handler: app.lambda_handler
636
+ layers:
637
+ - arn:aws:lambda:mx-central-1:610118373846:layer:AWSOpenTelemetryDistroPython:13
638
+ environment:
639
+ AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument
640
+ OTEL_SERVICE_NAME: ${self:service}
641
+ ```
642
+
643
+ Do not bundle `opentelemetry-*` packages in your function when using the ADOT
644
+ layer. The layer provides the OpenTelemetry runtime; bundling a different
645
+ version can conflict with the layer. Public ADOT layer ARNs are regional, so
646
+ use the ARN that matches your configured AWS region.
647
+
648
+ ```python
649
+ from modmex_lambda import Tracer
650
+
651
+ tracer = Tracer(service="orders")
652
+
653
+
654
+ @tracer.capture_lambda_handler
655
+ def lambda_handler(event, context):
656
+ return handle(event)
657
+
658
+
659
+ @tracer.capture_method(name="handle_order")
660
+ def handle(event):
661
+ tracer.set_attribute("tenant_id", event.get("tenant_id"))
662
+ return {"ok": True}
663
+ ```
664
+
665
+ If OpenTelemetry is not installed or configured, the tracer is a no-op. Use an
666
+ external OpenTelemetry setup, such as an ADOT Lambda layer or your own SDK
667
+ configuration, when spans should be exported.
668
+
669
+ When decorating route handlers, put the route decorator above
670
+ `capture_method`, so the router registers the traced function:
671
+
672
+ ```python
673
+ @app.post("/todos")
674
+ @tracer.capture_method(name="create_todo")
675
+ def create_todo(todo: Annotated[Todo, Body()]) -> dict:
676
+ return todo.model_dump()
677
+ ```
678
+
610
679
 
611
680
  ## Stream Handlers
612
681
 
@@ -584,6 +584,75 @@ The logger emits structured JSON, reads `LOG_LEVEL`, uses `SERVICE_NAME` or
584
584
  `AWS_LAMBDA_FUNCTION_NAME` when no service is passed, and can extract Lambda
585
585
  request IDs and API Gateway correlation IDs.
586
586
 
587
+ ## Tracing
588
+
589
+ Tracing is optional and lazy. The core package does not require
590
+ `opentelemetry-api`, does not initialize an SDK/exporter, and does not import
591
+ OpenTelemetry during `import modmex_lambda`.
592
+
593
+ `Tracer` uses OpenTelemetry when an OpenTelemetry runtime is available. For
594
+ Lambda, the recommended runtime is the AWS Distro for OpenTelemetry (ADOT)
595
+ Lambda layer.
596
+
597
+ The example below uses Serverless Framework. If you use another IaC tool, the
598
+ same pieces are required: enable Lambda tracing, attach the ADOT Python layer
599
+ for the AWS region where the function runs, and configure the OpenTelemetry
600
+ environment variables.
601
+
602
+ ```yaml
603
+ provider:
604
+ name: aws
605
+ runtime: python3.12
606
+ region: mx-central-1
607
+ tracing:
608
+ lambda: true
609
+
610
+ functions:
611
+ api:
612
+ handler: app.lambda_handler
613
+ layers:
614
+ - arn:aws:lambda:mx-central-1:610118373846:layer:AWSOpenTelemetryDistroPython:13
615
+ environment:
616
+ AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument
617
+ OTEL_SERVICE_NAME: ${self:service}
618
+ ```
619
+
620
+ Do not bundle `opentelemetry-*` packages in your function when using the ADOT
621
+ layer. The layer provides the OpenTelemetry runtime; bundling a different
622
+ version can conflict with the layer. Public ADOT layer ARNs are regional, so
623
+ use the ARN that matches your configured AWS region.
624
+
625
+ ```python
626
+ from modmex_lambda import Tracer
627
+
628
+ tracer = Tracer(service="orders")
629
+
630
+
631
+ @tracer.capture_lambda_handler
632
+ def lambda_handler(event, context):
633
+ return handle(event)
634
+
635
+
636
+ @tracer.capture_method(name="handle_order")
637
+ def handle(event):
638
+ tracer.set_attribute("tenant_id", event.get("tenant_id"))
639
+ return {"ok": True}
640
+ ```
641
+
642
+ If OpenTelemetry is not installed or configured, the tracer is a no-op. Use an
643
+ external OpenTelemetry setup, such as an ADOT Lambda layer or your own SDK
644
+ configuration, when spans should be exported.
645
+
646
+ When decorating route handlers, put the route decorator above
647
+ `capture_method`, so the router registers the traced function:
648
+
649
+ ```python
650
+ @app.post("/todos")
651
+ @tracer.capture_method(name="create_todo")
652
+ def create_todo(todo: Annotated[Todo, Body()]) -> dict:
653
+ return todo.model_dump()
654
+ ```
655
+
587
656
 
588
657
  ## Stream Handlers
589
658
 
@@ -16,6 +16,7 @@ __all__ = [
16
16
  "create_dependency_resolver",
17
17
  "default_dependency_resolver",
18
18
  "Logger",
19
+ "Tracer",
19
20
  "ModmexValidator",
20
21
  "ValidationError",
21
22
  ]
@@ -38,6 +39,7 @@ def __getattr__(name):
38
39
  "create_dependency_resolver": ("modmex_lambda.dependencies", "create_dependency_resolver"),
39
40
  "default_dependency_resolver": ("modmex_lambda.dependencies", "default_dependency_resolver"),
40
41
  "Logger": ("modmex_lambda.logging", "Logger"),
42
+ "Tracer": ("modmex_lambda.tracing", "Tracer"),
41
43
  "ModmexValidator": ("modmex_lambda.validation", "ModmexValidator"),
42
44
  "ValidationError": ("modmex_lambda.validation", "ValidationError"),
43
45
  }.get(name)
@@ -20,8 +20,13 @@ class GatewayResponseBuilder:
20
20
  self.json_serializer = json_serializer
21
21
 
22
22
  def serialize(self, event: BaseProxyEvent, cors: CORSConfig | None = None) -> dict[str, Any]:
23
- if self.response.is_json() and not isinstance(self.response.body, (str, bytes)):
24
- self.response.body = self.json_serializer(self.response.body)
23
+ if self.response.body is None:
24
+ self.response.body = ""
25
+ elif self.response.is_json() and not isinstance(self.response.body, (str, bytes)):
26
+ if hasattr(self.response.body, "model_dump_json"):
27
+ self.response.body = self.response.body.model_dump_json()
28
+ else:
29
+ self.response.body = self.json_serializer(self.response.body)
25
30
 
26
31
  self._handle_route_configuration(event, cors)
27
32
 
@@ -7,8 +7,11 @@ import os
7
7
  import sys
8
8
  import traceback
9
9
  from datetime import datetime, timezone
10
+ from threading import RLock
10
11
  from typing import Any, Callable, TextIO
11
12
 
13
+ from modmex_lambda.shared.json_encoder import JSONEncoder
14
+
12
15
 
13
16
  _LEVELS = {
14
17
  "DEBUG": 10,
@@ -19,6 +22,17 @@ _LEVELS = {
19
22
  }
20
23
 
21
24
 
25
+ class _LoggerJSONEncoder(JSONEncoder):
26
+ def default(self, obj: object) -> object:
27
+ if hasattr(obj, "model_dump"):
28
+ value = obj.model_dump()
29
+ try:
30
+ return json.loads(value)
31
+ except (TypeError, ValueError):
32
+ return value
33
+ return super().default(obj)
34
+
35
+
22
36
  class Logger:
23
37
  def __init__(
24
38
  self,
@@ -31,28 +45,36 @@ class Logger:
31
45
  ) -> None:
32
46
  self._service = service or os.getenv("SERVICE_NAME") or os.getenv("AWS_LAMBDA_FUNCTION_NAME") or "service"
33
47
  self._stream = stream or sys.stdout
34
- self._serialize = json_serializer or (lambda payload: json.dumps(payload, separators=(",", ":"), default=str))
48
+ self._serialize = json_serializer or self._serialize_payload
35
49
  self._correlation_id_header = correlation_id_header.lower()
36
50
  self._level = self._resolve_level(level)
37
51
  self._persistent_keys: dict[str, Any] = {}
38
52
  self._context: object | None = None
39
53
  self._event: dict[str, Any] | None = None
54
+ self._lock = RLock()
40
55
 
41
56
  def set_context(self, *, context: object | None = None, event: dict[str, Any] | None = None) -> None:
42
- self._context = context
43
- self._event = event
57
+ with self._lock:
58
+ self._context = context
59
+ self._event = event
44
60
 
45
61
  def append_keys(self, **kwargs: Any) -> None:
46
- self._persistent_keys.update(kwargs)
62
+ with self._lock:
63
+ self._persistent_keys.update(kwargs)
47
64
 
48
65
  def clear_state(self) -> None:
49
- self._persistent_keys.clear()
66
+ with self._lock:
67
+ self._persistent_keys.clear()
68
+ self._context = None
69
+ self._event = None
50
70
 
51
71
  def set_level(self, level: str | int) -> None:
52
- self._level = self._resolve_level(level)
72
+ with self._lock:
73
+ self._level = self._resolve_level(level)
53
74
 
54
75
  def is_enabled_for(self, level: str | int) -> bool:
55
- return self._resolve_level(level) >= self._level
76
+ with self._lock:
77
+ return self._resolve_level(level) >= self._level
56
78
 
57
79
  def debug(self, message: Any, *args: Any, **kwargs: Any) -> None:
58
80
  self._log("DEBUG", message, *args, **kwargs)
@@ -70,31 +92,32 @@ class Logger:
70
92
  self._log("CRITICAL", message, *args, exc_info=exc_info, **kwargs)
71
93
 
72
94
  def _log(self, level: str, message: Any, *args: Any, exc_info: bool = False, **kwargs: Any) -> None:
73
- if not self.is_enabled_for(level):
74
- return
95
+ with self._lock:
96
+ if not self.is_enabled_for(level):
97
+ return
75
98
 
76
- record: dict[str, Any] = {
77
- "timestamp": datetime.now(timezone.utc).isoformat(),
78
- "level": level,
79
- "service": self._service,
80
- "message": self._format_message(message, args),
81
- }
82
- record.update(self._persistent_keys)
99
+ record: dict[str, Any] = {
100
+ "timestamp": datetime.now(timezone.utc).isoformat(),
101
+ "level": level,
102
+ "service": self._service,
103
+ "message": self._format_message(message, args),
104
+ }
105
+ record.update(self._persistent_keys)
83
106
 
84
- request_id = self._extract_request_id()
85
- if request_id:
86
- record["request_id"] = request_id
107
+ request_id = self._extract_request_id()
108
+ if request_id:
109
+ record["request_id"] = request_id
87
110
 
88
- correlation_id = self._extract_correlation_id()
89
- if correlation_id and "correlation_id" not in record:
90
- record["correlation_id"] = correlation_id
111
+ correlation_id = self._extract_correlation_id()
112
+ if correlation_id and "correlation_id" not in record:
113
+ record["correlation_id"] = correlation_id
91
114
 
92
- record.update(kwargs)
115
+ record.update(kwargs)
93
116
 
94
- if exc_info:
95
- record["exception"] = traceback.format_exc()
117
+ if exc_info:
118
+ record["exception"] = traceback.format_exc()
96
119
 
97
- self._stream.write(self._serialize(record) + "\n")
120
+ self._stream.write(self._serialize(record) + "\n")
98
121
 
99
122
  def _resolve_level(self, level: str | int | None) -> int:
100
123
  if isinstance(level, int):
@@ -102,6 +125,12 @@ class Logger:
102
125
  level_name = (level or os.getenv("LOG_LEVEL") or "INFO").upper()
103
126
  return _LEVELS.get(level_name, _LEVELS["INFO"])
104
127
 
128
+ def _serialize_payload(self, payload: dict[str, Any]) -> str:
129
+ try:
130
+ return json.dumps(payload, separators=(",", ":"), cls=_LoggerJSONEncoder)
131
+ except TypeError:
132
+ return json.dumps(payload, separators=(",", ":"), default=str)
133
+
105
134
  def _format_message(self, message: Any, args: tuple[Any, ...]) -> Any:
106
135
  if not args:
107
136
  return message
@@ -11,10 +11,10 @@ from modmex_lambda.dependencies import (
11
11
  DependencyResolver,
12
12
  default_dependency_resolver,
13
13
  )
14
+ from modmex_lambda.logging import Logger
14
15
  from modmex_lambda.stream.flavors.iflavor import IFlavor
15
16
  from modmex_lambda.stream.operators.publisher import Publisher, PublisherOptions
16
17
  from modmex_lambda.stream.utils.contracts import Event
17
- from modmex_lambda.stream.utils.opt import DEFAULT_OPTIONS
18
18
 
19
19
 
20
20
  if TYPE_CHECKING:
@@ -39,7 +39,7 @@ class BaseFlavor(Generic[TEvent], IFlavor):
39
39
  dependency_resolver: Optional[DependencyResolver] = None,
40
40
  publisher_options: Optional[PublisherOptions] = None,
41
41
  ) -> None:
42
- self.logger = logger or DEFAULT_OPTIONS.get("logger")
42
+ self.logger = logger or Logger()
43
43
  self.connector = connector
44
44
  self.dependency_resolver = dependency_resolver
45
45
  self.publisher_options = publisher_options or {}
@@ -127,7 +127,7 @@ class BaseFlavor(Generic[TEvent], IFlavor):
127
127
  self._publisher = Publisher[TEvent](
128
128
  connector=self.connector or self.resolve(IEventBridgeConnector),
129
129
  logger=self.logger,
130
- bus_name=options.get("bus_name") or DEFAULT_OPTIONS.get("bus_name"),
130
+ bus_name=options.get("bus_name"),
131
131
  source=options.get("source"),
132
132
  event_field=options.get("event_field"),
133
133
  publish_request_entry_field=options.get("publish_request_entry_field"),
@@ -42,7 +42,7 @@ class Publisher(Generic[TEvent], IOperator[Uow[TEvent]]):
42
42
  logger: Optional[object] = None,
43
43
  ):
44
44
  self.connector = connector or Connector()
45
- self.bus_name = bus_name or os.getenv('BUS_NAME')
45
+ self.bus_name = bus_name or os.getenv('BUS_NAME') or 'undefined'
46
46
  self.source = source or os.getenv('BUS_SRC') or 'custom'
47
47
  self.event_field = event_field or 'event'
48
48
  self.publish_request_entry_field = publish_request_entry_field or 'publish_request_entry'
@@ -1,29 +1,45 @@
1
1
  import copy
2
2
  import multiprocessing
3
+ import os
3
4
  import threading
4
- from typing import Iterable, Optional
5
+ from typing import Any, Callable, Iterable, TypedDict
5
6
 
6
7
  from reactivex import Observable, from_list, operators as ops
7
8
  from reactivex.scheduler import ThreadPoolScheduler
8
9
 
9
10
  from modmex_lambda.stream.irules_registry import IRulesRegistry
11
+ from modmex_lambda.stream.utils.eventbridge import publish_to_event_bridge
10
12
  from modmex_lambda.stream.utils.faults import flush_faults
11
- from modmex_lambda.stream.utils.opt import DEFAULT_OPTIONS
12
13
  from modmex_lambda.logging import Logger
13
14
  from modmex_lambda.stream.utils.operators import tap
14
15
 
15
16
 
17
+ PublishFactory = Callable[[dict[str, Any]], Any]
18
+ BusName = str | Callable[[dict[str, Any]], str] | None
19
+
20
+
21
+ class RunOptions(TypedDict, total=False):
22
+ bus_name: BusName
23
+ publish: PublishFactory
24
+ logger: Any
25
+
26
+
27
+ def _publish_to_event_bridge(params: dict[str, Any]) -> Any:
28
+ return publish_to_event_bridge(**params)
29
+
30
+
16
31
  def run(
17
32
  events: Iterable,
18
33
  registry: IRulesRegistry,
19
- opt: Optional[dict] = None,
34
+ opt: RunOptions | None = None,
20
35
  on_next=None,
21
36
  on_error=None,
22
37
  on_completed=None,
23
38
  concurrency=True,
24
39
  ):
25
40
  opt = {
26
- **DEFAULT_OPTIONS,
41
+ 'bus_name': os.getenv('BUS_NAME'),
42
+ 'publish': _publish_to_event_bridge,
27
43
  **(opt or {}),
28
44
  }
29
45
  optimal_thread_count = multiprocessing.cpu_count()
@@ -1,4 +1,3 @@
1
- import os
2
1
  from modmex_lambda.logging import Logger
3
2
  from modmex_lambda.connectors.eventbridge import Connector
4
3
  from modmex_lambda.stream.operators.publisher import Publisher
@@ -6,12 +5,12 @@ from modmex_lambda.stream.operators.publisher import Publisher
6
5
  # pylint: disable=unused-argument,too-many-arguments
7
6
  def publish_to_event_bridge(
8
7
  logger=None,
9
- bus_name=os.getenv('BUS_NAME') or 'undefined',
10
- source=os.getenv('BUS_SRC') or 'custom',
11
- event_field='event',
12
- publish_request_entry_field='publish_request_entry',
13
- publish_request_field='publish_request',
14
- batch_size=os.getenv('PUBLISH_BATCH_SIZE') or os.getenv('BATCH_SIZE') or 10,
8
+ bus_name=None,
9
+ source=None,
10
+ event_field=None,
11
+ publish_request_entry_field=None,
12
+ publish_request_field=None,
13
+ batch_size=None,
15
14
  ):
16
15
  return Publisher(
17
16
  connector=Connector(),
@@ -0,0 +1,189 @@
1
+ """Lazy OpenTelemetry-compatible tracing helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ import os
7
+ from contextlib import contextmanager
8
+ from functools import wraps
9
+ from importlib import import_module
10
+ from typing import Any, Callable, Iterator, Mapping, TypeVar
11
+
12
+
13
+ F = TypeVar("F", bound=Callable[..., Any])
14
+
15
+
16
+ class _NoopSpan:
17
+ def set_attribute(self, _key: str, _value: Any) -> None:
18
+ return None
19
+
20
+ def add_event(self, _name: str, attributes: Mapping[str, Any] | None = None) -> None:
21
+ return None
22
+
23
+ def record_exception(self, _exception: BaseException) -> None:
24
+ return None
25
+
26
+
27
+ class Tracer:
28
+ """Tiny lazy tracer facade.
29
+
30
+ The core package does not depend on OpenTelemetry. If ``opentelemetry-api``
31
+ is unavailable, all operations are no-ops.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ *,
37
+ service: str | None = None,
38
+ enabled: bool = True,
39
+ tracer_provider: Any | None = None,
40
+ ) -> None:
41
+ self.service = service or os.getenv("SERVICE_NAME") or os.getenv("AWS_LAMBDA_FUNCTION_NAME") or "service"
42
+ self.enabled = enabled
43
+ self.tracer_provider = tracer_provider
44
+ self._tracer: Any | None = None
45
+ self._cold_start = True
46
+
47
+ def capture_method(
48
+ self,
49
+ func: F | None = None,
50
+ *,
51
+ name: str | None = None,
52
+ attributes: Mapping[str, Any] | None = None,
53
+ ) -> F | Callable[[F], F]:
54
+ def decorator(method: F) -> F:
55
+ span_name = name or method.__qualname__
56
+
57
+ if inspect.iscoroutinefunction(method):
58
+
59
+ @wraps(method)
60
+ async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
61
+ with self._start_span(span_name, attributes=attributes):
62
+ return await method(*args, **kwargs)
63
+
64
+ return async_wrapper # type: ignore[return-value]
65
+
66
+ @wraps(method)
67
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
68
+ with self._start_span(span_name, attributes=attributes):
69
+ return method(*args, **kwargs)
70
+
71
+ return wrapper # type: ignore[return-value]
72
+
73
+ if func is None:
74
+ return decorator
75
+ return decorator(func)
76
+
77
+ def capture_lambda_handler(
78
+ self,
79
+ func: F | None = None,
80
+ *,
81
+ name: str | None = None,
82
+ attributes: Mapping[str, Any] | None = None,
83
+ ) -> F | Callable[[F], F]:
84
+ def decorator(handler: F) -> F:
85
+ span_name = name or handler.__name__
86
+
87
+ @wraps(handler)
88
+ def wrapper(event: Any, context: Any, *args: Any, **kwargs: Any) -> Any:
89
+ with self._start_span(span_name, attributes=attributes) as span:
90
+ self._set_lambda_context_attributes(span, context)
91
+ return handler(event, context, *args, **kwargs)
92
+
93
+ return wrapper # type: ignore[return-value]
94
+
95
+ if func is None:
96
+ return decorator
97
+ return decorator(func)
98
+
99
+ def set_attribute(self, key: str, value: Any) -> None:
100
+ span = self._current_span()
101
+ span.set_attribute(key, value)
102
+
103
+ def add_event(self, name: str, attributes: Mapping[str, Any] | None = None) -> None:
104
+ span = self._current_span()
105
+ span.add_event(name, attributes=attributes)
106
+
107
+ @contextmanager
108
+ def _start_span(
109
+ self,
110
+ name: str,
111
+ *,
112
+ attributes: Mapping[str, Any] | None = None,
113
+ ) -> Iterator[Any]:
114
+ tracer = self._get_tracer()
115
+ if tracer is None:
116
+ yield _NoopSpan()
117
+ return
118
+
119
+ try:
120
+ span_context = tracer.start_as_current_span(name)
121
+ except AttributeError:
122
+ yield _NoopSpan()
123
+ return
124
+
125
+ with span_context as span:
126
+ for key, value in (attributes or {}).items():
127
+ span.set_attribute(key, value)
128
+ try:
129
+ yield span
130
+ except Exception as exc:
131
+ span.record_exception(exc)
132
+ self._set_error_status(span)
133
+ raise
134
+
135
+ def _get_tracer(self) -> Any | None:
136
+ if not self.enabled:
137
+ return None
138
+ if self._tracer is not None:
139
+ return self._tracer
140
+
141
+ try:
142
+ trace = import_module("opentelemetry.trace")
143
+ except ImportError:
144
+ return None
145
+
146
+ self._tracer = trace.get_tracer(
147
+ __name__,
148
+ tracer_provider=self.tracer_provider,
149
+ )
150
+ return self._tracer
151
+
152
+ def _current_span(self) -> Any:
153
+ if not self.enabled:
154
+ return _NoopSpan()
155
+
156
+ try:
157
+ trace = import_module("opentelemetry.trace")
158
+ except ImportError:
159
+ return _NoopSpan()
160
+
161
+ try:
162
+ return trace.get_current_span()
163
+ except AttributeError:
164
+ return _NoopSpan()
165
+
166
+ def _set_lambda_context_attributes(self, span: Any, context: Any) -> None:
167
+ if self._cold_start:
168
+ span.set_attribute("faas.coldstart", True)
169
+ self._cold_start = False
170
+ else:
171
+ span.set_attribute("faas.coldstart", False)
172
+
173
+ request_id = getattr(context, "aws_request_id", None)
174
+ if request_id:
175
+ span.set_attribute("faas.execution", str(request_id))
176
+
177
+ function_name = getattr(context, "function_name", None)
178
+ if function_name:
179
+ span.set_attribute("faas.name", str(function_name))
180
+
181
+ def _set_error_status(self, span: Any) -> None:
182
+ try:
183
+ trace = import_module("opentelemetry.trace")
184
+ span.set_status(trace.Status(trace.StatusCode.ERROR))
185
+ except (ImportError, AttributeError):
186
+ return
187
+
188
+
189
+ __all__ = ["Tracer"]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "modmex-lambda"
7
- version = "0.4.1"
7
+ version = "0.5.1"
8
8
  description = "Ultra-lightweight AWS Lambda utilities for API Gateway routing and event handling."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10,<4.0"