aws-durable-execution-sdk-python 1.4.0__tar.gz → 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. aws_durable_execution_sdk_python-1.5.0/.github/scripts/__init__.py +1 -0
  2. {aws_durable_execution_sdk_python-1.4.0/ops → aws_durable_execution_sdk_python-1.5.0/.github/scripts}/ci-checks.sh +3 -0
  3. aws_durable_execution_sdk_python-1.5.0/.github/scripts/lintcommit.py +265 -0
  4. {aws_durable_execution_sdk_python-1.4.0/ops → aws_durable_execution_sdk_python-1.5.0/.github/scripts}/parse_sdk_branch.py +0 -0
  5. aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/__init__.py +1 -0
  6. aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests/test_lintcommit.py +261 -0
  7. {aws_durable_execution_sdk_python-1.4.0/ops/__tests__ → aws_durable_execution_sdk_python-1.5.0/.github/scripts/tests}/test_parse_sdk_branch.py +0 -0
  8. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/ci.yml +10 -11
  9. aws_durable_execution_sdk_python-1.5.0/.github/workflows/deploy-examples.yml +134 -0
  10. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/integration-tests.yml +15 -35
  11. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-issues.yml +23 -0
  12. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-pr.yml +24 -0
  13. aws_durable_execution_sdk_python-1.5.0/.github/workflows/notify-release.yml +29 -0
  14. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/pypi-publish.yml +10 -2
  15. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/scorecard.yml +2 -2
  16. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/sync-package.yml +1 -1
  17. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/workflows/test-parser.yml +5 -5
  18. aws_durable_execution_sdk_python-1.5.0/.github/workflows/update-sam-template.yml +42 -0
  19. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.gitignore +6 -1
  20. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/AGENTS.md +1 -6
  21. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/CONTRIBUTING.md +65 -1
  22. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/PKG-INFO +6 -29
  23. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/README.md +4 -27
  24. aws_durable_execution_sdk_python-1.5.0/examples/cli.py +487 -0
  25. aws_durable_execution_sdk_python-1.5.0/examples/examples-catalog.json +607 -0
  26. aws_durable_execution_sdk_python-1.5.0/examples/scripts/generate_sam_template.py +106 -0
  27. aws_durable_execution_sdk_python-1.5.0/examples/src/__init__.py +3 -0
  28. aws_durable_execution_sdk_python-1.5.0/examples/src/block_example/block_example.py +47 -0
  29. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_concurrency.py +51 -0
  30. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_heartbeat.py +22 -0
  31. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_mixed_ops.py +35 -0
  32. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_serdes.py +76 -0
  33. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_simple.py +22 -0
  34. aws_durable_execution_sdk_python-1.5.0/examples/src/callback/callback_with_timeout.py +23 -0
  35. aws_durable_execution_sdk_python-1.5.0/examples/src/comprehensive_operations/comprehensive_operations.py +51 -0
  36. aws_durable_execution_sdk_python-1.5.0/examples/src/handler_error/handler_error.py +13 -0
  37. aws_durable_execution_sdk_python-1.5.0/examples/src/hello_world.py +62 -0
  38. aws_durable_execution_sdk_python-1.5.0/examples/src/logger_example/logger_example.py +64 -0
  39. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_completion.py +117 -0
  40. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations.py +23 -0
  41. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_operations_flat.py +23 -0
  42. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_batch_serdes.py +96 -0
  43. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_custom_serdes.py +63 -0
  44. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_failure_tolerance.py +53 -0
  45. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_item_namer.py +30 -0
  46. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_large_scale.py +64 -0
  47. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_max_concurrency.py +23 -0
  48. aws_durable_execution_sdk_python-1.5.0/examples/src/map/map_with_min_successful.py +43 -0
  49. aws_durable_execution_sdk_python-1.5.0/examples/src/no_replay_execution/no_replay_execution.py +15 -0
  50. aws_durable_execution_sdk_python-1.5.0/examples/src/none_results/none_results.py +31 -0
  51. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel.py +27 -0
  52. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_first_successful.py +27 -0
  53. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_flat.py +27 -0
  54. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_batch_serdes.py +97 -0
  55. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_custom_serdes.py +60 -0
  56. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_failure_tolerance.py +59 -0
  57. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_max_concurrency.py +25 -0
  58. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_named_branches.py +51 -0
  59. aws_durable_execution_sdk_python-1.5.0/examples/src/parallel/parallel_with_wait.py +24 -0
  60. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context.py +22 -0
  61. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_large_data.py +73 -0
  62. aws_durable_execution_sdk_python-1.5.0/examples/src/run_in_child_context/run_in_child_context_step_failure.py +50 -0
  63. aws_durable_execution_sdk_python-1.5.0/examples/src/simple_execution/simple_execution.py +18 -0
  64. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step.py +19 -0
  65. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_no_name.py +11 -0
  66. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_semantics_at_most_once.py +18 -0
  67. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_exponential_backoff.py +27 -0
  68. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_name.py +11 -0
  69. aws_durable_execution_sdk_python-1.5.0/examples/src/step/step_with_retry.py +46 -0
  70. aws_durable_execution_sdk_python-1.5.0/examples/src/step/steps_with_retry.py +81 -0
  71. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/multiple_wait.py +19 -0
  72. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait.py +11 -0
  73. aws_durable_execution_sdk_python-1.5.0/examples/src/wait/wait_with_name.py +12 -0
  74. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback.py +27 -0
  75. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_anonymous.py +20 -0
  76. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_child.py +42 -0
  77. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_heartbeat.py +33 -0
  78. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_mixed_ops.py +47 -0
  79. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_multiple_invocations.py +53 -0
  80. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_nested.py +66 -0
  81. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_serdes.py +90 -0
  82. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure.py +39 -0
  83. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_submitter_failure_catchable.py +52 -0
  84. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_callback/wait_for_callback_timeout.py +35 -0
  85. aws_durable_execution_sdk_python-1.5.0/examples/src/wait_for_condition/wait_for_condition.py +32 -0
  86. aws_durable_execution_sdk_python-1.5.0/examples/template.yaml +982 -0
  87. aws_durable_execution_sdk_python-1.5.0/examples/test/README.md +119 -0
  88. aws_durable_execution_sdk_python-1.5.0/examples/test/__init__.py +1 -0
  89. aws_durable_execution_sdk_python-1.5.0/examples/test/block_example/test_block_example.py +104 -0
  90. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_concurrency.py +83 -0
  91. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_heartbeat.py +51 -0
  92. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_mixed_ops.py +49 -0
  93. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_serdes.py +60 -0
  94. aws_durable_execution_sdk_python-1.5.0/examples/test/callback/test_callback_simple.py +47 -0
  95. aws_durable_execution_sdk_python-1.5.0/examples/test/comprehensive_operations/test_comprehensive_operations.py +94 -0
  96. aws_durable_execution_sdk_python-1.5.0/examples/test/conftest.py +268 -0
  97. aws_durable_execution_sdk_python-1.5.0/examples/test/handler_error/test_handler_error.py +32 -0
  98. aws_durable_execution_sdk_python-1.5.0/examples/test/logger_example/test_logger_example.py +35 -0
  99. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_completion.py +32 -0
  100. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations.py +42 -0
  101. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_operations_flat.py +39 -0
  102. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_batch_serdes.py +43 -0
  103. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_custom_serdes.py +48 -0
  104. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_failure_tolerance.py +52 -0
  105. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_item_namer.py +39 -0
  106. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_large_scale.py +36 -0
  107. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_max_concurrency.py +37 -0
  108. aws_durable_execution_sdk_python-1.5.0/examples/test/map/test_map_with_min_successful.py +70 -0
  109. aws_durable_execution_sdk_python-1.5.0/examples/test/no_replay_execution/test_no_replay_execution.py +52 -0
  110. aws_durable_execution_sdk_python-1.5.0/examples/test/none_results/test_none_results.py +51 -0
  111. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel.py +42 -0
  112. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_flat.py +42 -0
  113. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_batch_serdes.py +43 -0
  114. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_custom_serdes.py +46 -0
  115. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_failure_tolerance.py +49 -0
  116. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_max_concurrency.py +36 -0
  117. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_named_branches.py +57 -0
  118. aws_durable_execution_sdk_python-1.5.0/examples/test/parallel/test_parallel_with_wait.py +47 -0
  119. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context.py +27 -0
  120. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_large_data.py +36 -0
  121. aws_durable_execution_sdk_python-1.5.0/examples/test/run_in_child_context/test_run_in_child_context_step_failure.py +23 -0
  122. aws_durable_execution_sdk_python-1.5.0/examples/test/simple_execution/test_simple_execution.py +40 -0
  123. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step.py +24 -0
  124. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_permutations.py +75 -0
  125. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_semantics_at_most_once.py +32 -0
  126. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_step_with_retry.py +40 -0
  127. aws_durable_execution_sdk_python-1.5.0/examples/test/step/test_steps_with_retry.py +52 -0
  128. aws_durable_execution_sdk_python-1.5.0/examples/test/test_hello_world.py +24 -0
  129. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_multiple_wait.py +57 -0
  130. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait.py +27 -0
  131. aws_durable_execution_sdk_python-1.5.0/examples/test/wait/test_wait_permutations.py +25 -0
  132. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_anonymous.py +39 -0
  133. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_child.py +73 -0
  134. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_failure.py +27 -0
  135. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_heartbeat.py +62 -0
  136. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_mixed_ops.py +52 -0
  137. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_multiple_invocations.py +74 -0
  138. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_nested.py +101 -0
  139. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_serdes.py +66 -0
  140. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure.py +32 -0
  141. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_submitter_failure_catchable.py +28 -0
  142. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_success.py +25 -0
  143. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_callback/test_wait_for_callback_timeout.py +32 -0
  144. aws_durable_execution_sdk_python-1.5.0/examples/test/wait_for_condition/test_wait_for_condition.py +24 -0
  145. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/pyproject.toml +50 -2
  146. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/__about__.py +1 -1
  147. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/__init__.py +5 -0
  148. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/executor.py +34 -13
  149. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/config.py +108 -14
  150. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/context.py +120 -13
  151. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/exceptions.py +100 -42
  152. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/execution.py +57 -51
  153. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/lambda_service.py +12 -0
  154. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/child.py +43 -29
  155. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/map.py +15 -2
  156. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/parallel.py +25 -4
  157. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/step.py +2 -2
  158. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/state.py +74 -9
  159. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/types.py +3 -1
  160. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/concurrency_test.py +292 -35
  161. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/config_test.py +0 -11
  162. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/context_test.py +317 -1
  163. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/exceptions_test.py +145 -34
  164. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/execution_test.py +230 -97
  165. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/lambda_service_test.py +77 -0
  166. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/child_test.py +248 -11
  167. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/map_test.py +163 -11
  168. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/parallel_test.py +151 -15
  169. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/step_test.py +55 -0
  170. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/state_test.py +187 -1
  171. aws_durable_execution_sdk_python-1.4.0/.github/workflows/lintcommit.js +0 -178
  172. aws_durable_execution_sdk_python-1.4.0/.github/workflows/notify_slack.yml +0 -39
  173. aws_durable_execution_sdk_python-1.4.0/docs/advanced/error-handling.md +0 -955
  174. aws_durable_execution_sdk_python-1.4.0/docs/advanced/serialization.md +0 -771
  175. aws_durable_execution_sdk_python-1.4.0/docs/advanced/testing-modes.md +0 -495
  176. aws_durable_execution_sdk_python-1.4.0/docs/api-reference/.gitkeep +0 -1
  177. aws_durable_execution_sdk_python-1.4.0/docs/architecture.md +0 -505
  178. aws_durable_execution_sdk_python-1.4.0/docs/best-practices.md +0 -850
  179. aws_durable_execution_sdk_python-1.4.0/docs/core/callbacks.md +0 -877
  180. aws_durable_execution_sdk_python-1.4.0/docs/core/child-contexts.md +0 -703
  181. aws_durable_execution_sdk_python-1.4.0/docs/core/invoke.md +0 -774
  182. aws_durable_execution_sdk_python-1.4.0/docs/core/logger.md +0 -737
  183. aws_durable_execution_sdk_python-1.4.0/docs/core/map.md +0 -589
  184. aws_durable_execution_sdk_python-1.4.0/docs/core/parallel.md +0 -911
  185. aws_durable_execution_sdk_python-1.4.0/docs/core/steps.md +0 -597
  186. aws_durable_execution_sdk_python-1.4.0/docs/core/wait.md +0 -445
  187. aws_durable_execution_sdk_python-1.4.0/docs/getting-started.md +0 -293
  188. aws_durable_execution_sdk_python-1.4.0/docs/index.md +0 -205
  189. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/.gitkeep +0 -1
  190. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/basic-tests.md +0 -701
  191. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/complex-workflows.md +0 -675
  192. aws_durable_execution_sdk_python-1.4.0/docs/testing-patterns/stores.md +0 -262
  193. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  194. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  195. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
  196. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  197. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/.github/dependabot.yml +0 -0
  198. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/CODE_OF_CONDUCT.md +0 -0
  199. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/LICENSE +0 -0
  200. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/NOTICE +0 -0
  201. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/.gitignore +0 -0
  202. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/__init__.py +0 -0
  203. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/concurrency/models.py +0 -0
  204. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/identifier.py +0 -0
  205. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/logger.py +0 -0
  206. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/__init__.py +0 -0
  207. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/base.py +0 -0
  208. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/callback.py +0 -0
  209. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/invoke.py +0 -0
  210. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/wait.py +0 -0
  211. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/operation/wait_for_condition.py +0 -0
  212. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/py.typed +0 -0
  213. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/retries.py +0 -0
  214. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/serdes.py +0 -0
  215. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/suspend.py +0 -0
  216. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/threading.py +0 -0
  217. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/src/aws_durable_execution_sdk_python/waits.py +0 -0
  218. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/__init__.py +0 -0
  219. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/durable_executions_python_language_sdk_test.py +0 -0
  220. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/__init__.py +0 -0
  221. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/checkpoint_response_int_test.py +0 -0
  222. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/execution_int_test.py +0 -0
  223. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/e2e/map_with_concurrent_waits_int_test.py +0 -0
  224. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/logger_test.py +0 -0
  225. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/__init__.py +0 -0
  226. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/base_test.py +0 -0
  227. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/callback_test.py +0 -0
  228. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/invoke_test.py +0 -0
  229. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/wait_for_condition_test.py +0 -0
  230. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/operation/wait_test.py +0 -0
  231. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/retries_test.py +0 -0
  232. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/serdes_test.py +0 -0
  233. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/suspend_test.py +0 -0
  234. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/test_helpers.py +0 -0
  235. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/threading_test.py +0 -0
  236. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/types_test.py +0 -0
  237. {aws_durable_execution_sdk_python-1.4.0 → aws_durable_execution_sdk_python-1.5.0}/tests/waits_test.py +0 -0
@@ -13,3 +13,6 @@ echo SUCCESS: typings
13
13
  # static analysis
14
14
  hatch fmt
15
15
  echo SUCCESS: linting/fmt
16
+
17
+ # commit message validation
18
+ hatch run python .github/scripts/lintcommit.py
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env python3
2
+ # Checks that commit messages conform to conventional commits
3
+ # (https://www.conventionalcommits.org/).
4
+ #
5
+ # To run tests:
6
+ #
7
+ # python -m pytest .github/scripts/tests/test_lintcommit.py
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import re
13
+ import subprocess
14
+ import sys
15
+ from dataclasses import dataclass, field
16
+
17
+ TYPES: set[str] = {
18
+ "build",
19
+ "chore",
20
+ "ci",
21
+ "deps",
22
+ "docs",
23
+ "feat",
24
+ "fix",
25
+ "perf",
26
+ "refactor",
27
+ "style",
28
+ "test",
29
+ }
30
+
31
+ MAX_SUBJECT_LENGTH: int = 50
32
+ MAX_SCOPE_LENGTH: int = 30
33
+ MAX_BODY_LINE_LENGTH: int = 72
34
+
35
+
36
+ def validate_subject(subject_line: str) -> str | None:
37
+ """Validate a commit message subject line.
38
+
39
+ Returns None if valid, else an error message string.
40
+ """
41
+ parts: list[str] = subject_line.split(":", maxsplit=1)
42
+
43
+ if len(parts) < 2:
44
+ return "missing colon (:) char"
45
+
46
+ type_scope: str = parts[0]
47
+ subject: str = parts[1].strip()
48
+
49
+ # Parse type and optional scope: type or type(scope)
50
+ scope: str | None = None
51
+ commit_type: str = type_scope
52
+
53
+ if "(" in type_scope:
54
+ paren_start: int = type_scope.index("(")
55
+ commit_type = type_scope[:paren_start]
56
+
57
+ if not type_scope.endswith(")"):
58
+ return "must be formatted like type(scope):"
59
+
60
+ scope = type_scope[paren_start + 1 : -1]
61
+
62
+ if " " in commit_type:
63
+ return f'type contains whitespace: "{commit_type}"'
64
+
65
+ if commit_type not in TYPES:
66
+ return f'invalid type "{commit_type}"'
67
+
68
+ if scope is not None:
69
+ if len(scope) > MAX_SCOPE_LENGTH:
70
+ return f"invalid scope (must be <={MAX_SCOPE_LENGTH} chars)"
71
+
72
+ if re.search(r"[^- a-z0-9]", scope):
73
+ return f'invalid scope (must be lowercase, ascii only): "{scope}"'
74
+
75
+ if len(subject) == 0:
76
+ return "empty subject"
77
+
78
+ if len(subject) > MAX_SUBJECT_LENGTH:
79
+ return f"invalid subject (must be <={MAX_SUBJECT_LENGTH} chars)"
80
+
81
+ if subject.endswith("."):
82
+ return "subject must not end with a period"
83
+
84
+ return None
85
+
86
+
87
+ def validate_body(body: str) -> list[str]:
88
+ """Validate the body of a commit message.
89
+
90
+ Returns a list of warnings (not hard errors) for body issues.
91
+ """
92
+ warnings: list[str] = []
93
+ for i, line in enumerate(body.splitlines(), start=1):
94
+ if len(line) > MAX_BODY_LINE_LENGTH:
95
+ warnings.append(
96
+ f"body line {i} exceeds {MAX_BODY_LINE_LENGTH} chars ({len(line)} chars)"
97
+ )
98
+ return warnings
99
+
100
+
101
+ def validate_message(message: str) -> tuple[str | None, list[str]]:
102
+ """Validate a full commit message (subject + optional body).
103
+
104
+ Returns (error, warnings) where error is None if the subject is valid.
105
+ """
106
+ lines: list[str] = message.strip().splitlines()
107
+ if not lines:
108
+ return ("empty commit message", [])
109
+
110
+ subject_line: str = lines[0]
111
+ error: str | None = validate_subject(subject_line)
112
+
113
+ warnings: list[str] = []
114
+ # Check for blank line between subject and body
115
+ body_start: int = 2
116
+ if len(lines) > 1 and lines[1].strip() != "":
117
+ warnings.append("missing blank line between subject and body")
118
+ body_start = 1
119
+
120
+ if len(lines) > body_start:
121
+ body: str = "\n".join(lines[body_start:])
122
+ warnings.extend(validate_body(body))
123
+
124
+ return (error, warnings)
125
+
126
+
127
+ @dataclass
128
+ class CommitResult:
129
+ """Result of validating a single commit."""
130
+
131
+ sha: str
132
+ subject: str
133
+ error: str | None = None
134
+ warnings: list[str] = field(default_factory=list)
135
+
136
+
137
+ @dataclass
138
+ class LintResult:
139
+ """Result of linting a range of commits."""
140
+
141
+ commits: list[CommitResult] = field(default_factory=list)
142
+ skipped: bool = False
143
+ skip_reason: str = ""
144
+ empty: bool = False
145
+ git_error: str = ""
146
+
147
+ @property
148
+ def has_errors(self) -> bool:
149
+ return bool(self.git_error) or any(c.error for c in self.commits)
150
+
151
+
152
+ def lint_range(git_range: str, *, skip_dirty_check: bool = False) -> LintResult:
153
+ """Validate commit messages in a git range (e.g. 'origin/main..HEAD').
154
+
155
+ Args:
156
+ git_range: A git revision range like 'origin/main..HEAD'.
157
+ skip_dirty_check: When True, skip the uncommitted changes check
158
+ (useful in CI where the worktree may be clean by definition).
159
+
160
+ Returns:
161
+ A LintResult with per-commit validation results.
162
+ """
163
+ if not skip_dirty_check:
164
+ status = subprocess.run(
165
+ ["git", "status", "--porcelain"],
166
+ capture_output=True,
167
+ text=True,
168
+ )
169
+ if status.stdout.strip():
170
+ return LintResult(
171
+ skipped=True,
172
+ skip_reason=(
173
+ "uncommitted changes detected, skipping commit message validation.\n"
174
+ "Commit your changes and re-run to validate."
175
+ ),
176
+ )
177
+
178
+ result = subprocess.run(
179
+ ["git", "log", "--no-merges", git_range, "-z", "--format=%H%n%B"],
180
+ capture_output=True,
181
+ text=True,
182
+ )
183
+ if result.returncode != 0:
184
+ return LintResult(git_error=result.stderr.strip())
185
+
186
+ if not result.stdout.strip():
187
+ return LintResult(empty=True)
188
+
189
+ commits: list[CommitResult] = []
190
+ for record in result.stdout.split("\0"):
191
+ if not record.strip():
192
+ continue
193
+ sha, _, message = record.partition("\n")
194
+ message = message.strip()
195
+ if not message:
196
+ continue
197
+
198
+ error, warnings = validate_message(message)
199
+ subject = message.splitlines()[0]
200
+ commits.append(
201
+ CommitResult(
202
+ sha=sha[:7],
203
+ subject=subject,
204
+ error=error,
205
+ warnings=warnings,
206
+ )
207
+ )
208
+
209
+ return LintResult(commits=commits)
210
+
211
+
212
+ def write_output(lint_result: LintResult, git_range: str) -> None:
213
+ """Write lint results to stdout/stderr."""
214
+ if lint_result.skipped:
215
+ print(f"WARNING: {lint_result.skip_reason}")
216
+ return
217
+
218
+ if lint_result.git_error:
219
+ print(f"git log failed: {lint_result.git_error}", file=sys.stderr)
220
+ return
221
+
222
+ if lint_result.empty:
223
+ print(f"No commits in range {git_range}")
224
+ return
225
+
226
+ for commit in lint_result.commits:
227
+ if commit.error:
228
+ print(f"FAIL {commit.sha}: {commit.subject}", file=sys.stderr)
229
+ print(f" Error: {commit.error}", file=sys.stderr)
230
+ else:
231
+ print(f"PASS {commit.sha}: {commit.subject}")
232
+
233
+ for warning in commit.warnings:
234
+ print(f" Warning: {warning}")
235
+
236
+
237
+ def run_range(git_range: str, *, skip_dirty_check: bool = False) -> None:
238
+ """Validate commit messages in a git range and exit on errors."""
239
+ lint_result = lint_range(git_range, skip_dirty_check=skip_dirty_check)
240
+ write_output(lint_result, git_range)
241
+ if lint_result.has_errors:
242
+ sys.exit(1)
243
+
244
+
245
+ def main() -> None:
246
+ parser = argparse.ArgumentParser(
247
+ description="Lint commit messages for conventional commits compliance."
248
+ )
249
+ parser.add_argument(
250
+ "--range",
251
+ default=None,
252
+ dest="git_range",
253
+ help="Validate all commits in a git revision range (e.g. 'origin/main..HEAD'). "
254
+ "Skips the uncommitted-changes check (useful in CI).",
255
+ )
256
+ args = parser.parse_args()
257
+
258
+ if args.git_range is not None:
259
+ run_range(args.git_range, skip_dirty_check=True)
260
+ else:
261
+ run_range("origin/main..HEAD")
262
+
263
+
264
+ if __name__ == "__main__":
265
+ main()
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from subprocess import CompletedProcess
8
+ from unittest.mock import patch
9
+
10
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
11
+
12
+ import pytest
13
+
14
+ from lintcommit import lint_range, validate_message, validate_subject
15
+
16
+
17
+ # region validate_subject: valid subjects
18
+
19
+
20
+ def test_valid_feat() -> None:
21
+ assert validate_subject("feat: add new feature") is None
22
+
23
+
24
+ def test_valid_fix() -> None:
25
+ assert validate_subject("fix: resolve issue") is None
26
+
27
+
28
+ def test_valid_fix_with_scope() -> None:
29
+ assert validate_subject("fix(sdk): resolve issue") is None
30
+
31
+
32
+ def test_valid_build() -> None:
33
+ assert validate_subject("build: update build process") is None
34
+
35
+
36
+ def test_valid_chore() -> None:
37
+ assert validate_subject("chore: update dependencies") is None
38
+
39
+
40
+ def test_valid_ci() -> None:
41
+ assert validate_subject("ci: configure ci/cd") is None
42
+
43
+
44
+ def test_valid_deps() -> None:
45
+ assert validate_subject("deps: bump aws-sdk group with 5 updates") is None
46
+
47
+
48
+ def test_valid_docs() -> None:
49
+ assert validate_subject("docs: update documentation") is None
50
+
51
+
52
+ def test_valid_feat_with_scope() -> None:
53
+ assert validate_subject("feat(sdk): add new feature") is None
54
+
55
+
56
+ def test_valid_feat_scope_bar() -> None:
57
+ assert validate_subject("feat(sdk): bar") is None
58
+
59
+
60
+ def test_valid_feat_foo() -> None:
61
+ assert validate_subject("feat: foo") is None
62
+
63
+
64
+ def test_valid_fix_foo() -> None:
65
+ assert validate_subject("fix: foo") is None
66
+
67
+
68
+ # region validate_subject: invalid subjects
69
+
70
+
71
+ def test_invalid_type() -> None:
72
+ assert validate_subject("config: foo") == 'invalid type "config"'
73
+
74
+
75
+ def test_missing_colon() -> None:
76
+ assert validate_subject("invalid title") == "missing colon (:) char"
77
+
78
+
79
+ def test_period_at_end() -> None:
80
+ assert validate_subject("feat: add thing.") == "subject must not end with a period"
81
+
82
+
83
+ def test_empty_subject() -> None:
84
+ assert validate_subject("feat: ") == "empty subject"
85
+
86
+
87
+ def test_subject_too_long() -> None:
88
+ long_subject: str = "feat: " + "a" * 51
89
+ result = validate_subject(long_subject)
90
+ assert result is not None
91
+ assert "invalid subject" in result
92
+
93
+
94
+ def test_type_with_whitespace() -> None:
95
+ assert validate_subject("fe at: foo") == 'type contains whitespace: "fe at"'
96
+
97
+
98
+ def test_scope_not_closed() -> None:
99
+ assert validate_subject("feat(sdk: foo") == "must be formatted like type(scope):"
100
+
101
+
102
+ def test_scope_too_long() -> None:
103
+ long_scope: str = "a" * 31
104
+ result = validate_subject(f"feat({long_scope}): foo")
105
+ assert result is not None
106
+ assert "invalid scope" in result
107
+
108
+
109
+ def test_scope_uppercase() -> None:
110
+ result = validate_subject("feat(SDK): foo")
111
+ assert result is not None
112
+ assert "invalid scope" in result
113
+
114
+
115
+ def test_subject_uppercase() -> None:
116
+ assert validate_subject("feat: Add new feature") == "subject must be lowercase"
117
+
118
+
119
+ # region validate_message
120
+
121
+
122
+ def test_valid_subject_only() -> None:
123
+ error, warnings = validate_message("feat: add thing")
124
+ assert error is None
125
+ assert warnings == []
126
+
127
+
128
+ def test_valid_with_body() -> None:
129
+ error, warnings = validate_message("feat: add thing\n\nThis is the body.")
130
+ assert error is None
131
+ assert warnings == []
132
+
133
+
134
+ def test_missing_blank_line() -> None:
135
+ _, warnings = validate_message("feat: add thing\nNo blank line.")
136
+ assert "missing blank line between subject and body" in warnings
137
+
138
+
139
+ def test_missing_blank_line_body_still_checked() -> None:
140
+ _, warnings = validate_message("feat: add thing\n" + "x" * 80)
141
+ assert "missing blank line between subject and body" in warnings
142
+ assert any("exceeds 72 chars" in w for w in warnings), (
143
+ "body line length should be checked even without blank line"
144
+ )
145
+
146
+
147
+ def test_long_body_line() -> None:
148
+ _, warnings = validate_message("feat: add thing\n\n" + "x" * 80)
149
+ assert len(warnings) == 1
150
+ assert "exceeds 72 chars" in warnings[0]
151
+
152
+
153
+ def test_empty_message() -> None:
154
+ error, _ = validate_message("")
155
+ assert error == "empty commit message"
156
+
157
+
158
+ def test_invalid_subject_in_message() -> None:
159
+ error, _ = validate_message("invalid title")
160
+ assert error == "missing colon (:) char"
161
+
162
+
163
+ # region lint_range
164
+
165
+
166
+ def _make_git_log_output(*messages: str) -> str:
167
+ """Build fake ``git log --no-merges -z --format=%H%n%B`` output.
168
+
169
+ Records are separated by null characters.
170
+ """
171
+ records: list[str] = []
172
+ for i, msg in enumerate(messages):
173
+ sha = f"abc{i:04d}" + "0" * 33 # 40-char fake SHA
174
+ records.append(f"{sha}\n{msg}\n")
175
+ return "\0".join(records)
176
+
177
+
178
+ def _completed(
179
+ stdout: str = "", stderr: str = "", returncode: int = 0
180
+ ) -> CompletedProcess[str]:
181
+ """Shorthand for a ``subprocess.CompletedProcess``."""
182
+ return CompletedProcess(
183
+ args=[], returncode=returncode, stdout=stdout, stderr=stderr
184
+ )
185
+
186
+
187
+ @patch("subprocess.run")
188
+ def test_lint_range_all_valid(mock_run) -> None:
189
+ log_output = _make_git_log_output(
190
+ "feat: add new feature",
191
+ "fix(sdk): resolve issue",
192
+ )
193
+ mock_run.return_value = _completed(stdout=log_output)
194
+
195
+ result = lint_range("origin/main..HEAD", skip_dirty_check=True)
196
+
197
+ assert not result.has_errors
198
+ assert len(result.commits) == 2
199
+ assert all(c.error is None for c in result.commits)
200
+
201
+
202
+ @patch("subprocess.run")
203
+ def test_lint_range_with_invalid_commit(mock_run) -> None:
204
+ log_output = _make_git_log_output(
205
+ "feat: add new feature",
206
+ "bad commit no colon",
207
+ )
208
+ mock_run.return_value = _completed(stdout=log_output)
209
+
210
+ result = lint_range("origin/main..HEAD", skip_dirty_check=True)
211
+
212
+ assert result.has_errors
213
+ assert result.commits[0].error is None
214
+ assert result.commits[1].error == "missing colon (:) char"
215
+
216
+
217
+ @patch("subprocess.run")
218
+ def test_lint_range_empty(mock_run) -> None:
219
+ mock_run.return_value = _completed(stdout="")
220
+
221
+ result = lint_range("origin/main..HEAD", skip_dirty_check=True)
222
+
223
+ assert result.empty
224
+ assert not result.has_errors
225
+
226
+
227
+ @patch("subprocess.run")
228
+ def test_lint_range_git_failure(mock_run) -> None:
229
+ mock_run.return_value = _completed(returncode=1, stderr="fatal: bad range")
230
+
231
+ result = lint_range("bad..range", skip_dirty_check=True)
232
+
233
+ assert result.has_errors
234
+ assert result.git_error == "fatal: bad range"
235
+
236
+
237
+ @patch("subprocess.run")
238
+ def test_lint_range_dirty_worktree_skips(mock_run) -> None:
239
+ """When skip_dirty_check=False and worktree is dirty, validation is skipped."""
240
+ mock_run.return_value = _completed(stdout=" M .github/scripts/lintcommit.py\n")
241
+
242
+ result = lint_range("origin/main..HEAD", skip_dirty_check=False)
243
+
244
+ assert result.skipped
245
+ assert "uncommitted changes" in result.skip_reason
246
+ # git log should never have been called (only git status)
247
+ mock_run.assert_called_once()
248
+
249
+
250
+ @patch("subprocess.run")
251
+ def test_lint_range_warnings_collected(mock_run) -> None:
252
+ log_output = _make_git_log_output(
253
+ "feat: add thing\n\n" + "x" * 80,
254
+ )
255
+ mock_run.return_value = _completed(stdout=log_output)
256
+
257
+ result = lint_range("origin/main..HEAD", skip_dirty_check=True)
258
+
259
+ assert not result.has_errors
260
+ assert len(result.commits) == 1
261
+ assert any("exceeds 72 chars" in w for w in result.commits[0].warnings)
@@ -13,21 +13,20 @@ on:
13
13
 
14
14
  jobs:
15
15
  lint-commits:
16
- # Note: To re-run `lint-commits` after fixing the PR title, close-and-reopen the PR.
16
+ if: github.event_name == 'pull_request'
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
20
- - name: Use Node.js
21
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
22
- with:
23
- node-version: 22.x
24
- - name: Check PR title
25
- run: |
26
- node "$GITHUB_WORKSPACE/.github/workflows/lintcommit.js"
19
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
20
+ with:
21
+ fetch-depth: 0
22
+ - name: Set up Python
23
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
24
+ with:
25
+ python-version: "3.12"
26
+ - name: Lint commit messages
27
+ run: python .github/scripts/lintcommit.py --range "origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}"
27
28
 
28
29
  build:
29
- needs: lint-commits
30
-
31
30
  runs-on: ubuntu-latest
32
31
  strategy:
33
32
  fail-fast: false