cadence-python-client 0.2.3__tar.gz → 0.3.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 (203) hide show
  1. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.cursorrules +1 -1
  2. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/ci_checks.yml +2 -2
  3. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/Makefile +2 -2
  4. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/PKG-INFO +7 -15
  5. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/README.md +4 -8
  6. cadence_python_client-0.3.0/cadence/_internal/workflow/context.py +305 -0
  7. cadence_python_client-0.3.0/cadence/_internal/workflow/memo.py +40 -0
  8. cadence_python_client-0.3.0/cadence/_internal/workflow/statemachine/child_workflow_execution_state_machine.py +184 -0
  9. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/decision_manager.py +78 -13
  10. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/event_dispatcher.py +30 -5
  11. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/nondeterminism.py +88 -0
  12. cadence_python_client-0.3.0/cadence/_internal/workflow/statemachine/signal_external_workflow_state_machine.py +74 -0
  13. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2.py +13 -31
  14. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2.pyi +2 -31
  15. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2.py +8 -8
  16. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2.pyi +6 -2
  17. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2.py +32 -32
  18. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2.pyi +4 -2
  19. cadence_python_client-0.3.0/cadence/api/v1/tasklist_pb2.py +78 -0
  20. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/tasklist_pb2.pyi +4 -2
  21. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/client.py +20 -14
  22. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/error.py +39 -0
  23. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_decision_task_handler.py +21 -0
  24. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/workflow.py +136 -0
  25. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/PKG-INFO +7 -15
  26. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/SOURCES.txt +9 -1
  27. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/requires.txt +0 -4
  28. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/pyproject.toml +2 -6
  29. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_child_workflow_execution_state_machine.py +331 -0
  30. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_decision_manager.py +535 -0
  31. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_nondeterminism.py +90 -0
  32. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_signal_external_workflow_state_machine.py +101 -0
  33. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_context_child_workflow.py +452 -0
  34. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_context_signal_child_workflow.py +256 -0
  35. cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_memo.py +53 -0
  36. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_client_workflow.py +26 -0
  37. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_schedule.py +37 -8
  38. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_task_handler.py +32 -6
  39. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_task_handler_integration.py +33 -5
  40. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/docker-compose.yml +1 -2
  41. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/nondeterminism/test_nondeterministic_workflows.py +8 -9
  42. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/test_schedule.py +54 -36
  43. cadence_python_client-0.3.0/tests/integration_tests/workflow/test_child_workflow.py +231 -0
  44. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_signals.py +2 -1
  45. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/uv.lock +3 -173
  46. cadence_python_client-0.2.3/cadence/_internal/workflow/context.py +0 -148
  47. cadence_python_client-0.2.3/cadence/api/v1/tasklist_pb2.py +0 -78
  48. cadence_python_client-0.2.3/scripts/dev.py +0 -187
  49. cadence_python_client-0.2.3/tests/cadence/_internal/workflow/statemachine/test_decision_manager.py +0 -190
  50. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/dco.yml +0 -0
  51. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/pull_request_template.md +0 -0
  52. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/python-publish.yml +0 -0
  53. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/semantic-pr.yml +0 -0
  54. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.gitignore +0 -0
  55. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.gitmodules +0 -0
  56. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/CONTRIBUTING.md +0 -0
  57. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/LICENSE +0 -0
  58. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/NOTICE +0 -0
  59. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/__init__.py +0 -0
  60. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/__init__.py +0 -0
  61. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/__init__.py +0 -0
  62. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_activity_executor.py +0 -0
  63. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_context.py +0 -0
  64. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_definition.py +0 -0
  65. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_heartbeat.py +0 -0
  66. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/fn_signature.py +0 -0
  67. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/__init__.py +0 -0
  68. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/error.py +0 -0
  69. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/retry.py +0 -0
  70. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/yarpc.py +0 -0
  71. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/__init__.py +0 -0
  72. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/active_cluster_selection_policy.py +0 -0
  73. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/decision_events_iterator.py +0 -0
  74. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/deterministic_event_loop.py +0 -0
  75. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/history_event_iterator.py +0 -0
  76. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/retry_policy.py +0 -0
  77. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/__init__.py +0 -0
  78. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/activity_state_machine.py +0 -0
  79. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/cancellation.py +0 -0
  80. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/completion_state_machine.py +0 -0
  81. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/decision_state_machine.py +0 -0
  82. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/timer_state_machine.py +0 -0
  83. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/waiter.py +0 -0
  84. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/workflow_engine.py +0 -0
  85. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/workflow_instance.py +0 -0
  86. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/activity.py +0 -0
  87. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/__init__.py +0 -0
  88. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2_grpc.py +0 -0
  89. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2.py +0 -0
  90. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2.pyi +0 -0
  91. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2_grpc.py +0 -0
  92. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2.py +0 -0
  93. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2.pyi +0 -0
  94. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2_grpc.py +0 -0
  95. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2.py +0 -0
  96. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2.pyi +0 -0
  97. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2_grpc.py +0 -0
  98. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2.py +0 -0
  99. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2.pyi +0 -0
  100. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2_grpc.py +0 -0
  101. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2.py +0 -0
  102. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2.pyi +0 -0
  103. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2_grpc.py +0 -0
  104. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2_grpc.py +0 -0
  105. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2_grpc.py +0 -0
  106. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2.py +0 -0
  107. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2.pyi +0 -0
  108. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2_grpc.py +0 -0
  109. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2.py +0 -0
  110. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2.pyi +0 -0
  111. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2_grpc.py +0 -0
  112. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2.py +0 -0
  113. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2.pyi +0 -0
  114. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2_grpc.py +0 -0
  115. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2.py +0 -0
  116. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2.pyi +0 -0
  117. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2_grpc.py +0 -0
  118. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2.py +0 -0
  119. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2.pyi +0 -0
  120. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2_grpc.py +0 -0
  121. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/tasklist_pb2_grpc.py +0 -0
  122. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2.py +0 -0
  123. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2.pyi +0 -0
  124. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2_grpc.py +0 -0
  125. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2.py +0 -0
  126. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2.pyi +0 -0
  127. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2_grpc.py +0 -0
  128. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/__init__.py +0 -0
  129. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/README.md +0 -0
  130. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/__init__.py +0 -0
  131. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_agent_runner.py +0 -0
  132. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_handoff.py +0 -0
  133. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_model.py +0 -0
  134. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_registry.py +0 -0
  135. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_tool.py +0 -0
  136. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/images/cadence-web-agent-run.jpg +0 -0
  137. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/openai_activities.py +0 -0
  138. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/pydantic_data_converter.py +0 -0
  139. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/data_converter.py +0 -0
  140. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/__init__.py +0 -0
  141. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/constants.py +0 -0
  142. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/metrics.py +0 -0
  143. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/prometheus.py +0 -0
  144. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/query.py +0 -0
  145. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/__init__.py +0 -0
  146. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/client_example.py +0 -0
  147. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/grpc_usage_example.py +0 -0
  148. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/simple_usage_example.py +0 -0
  149. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/signal.py +0 -0
  150. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/__init__.py +0 -0
  151. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_activity.py +0 -0
  152. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_base_task_handler.py +0 -0
  153. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_decision.py +0 -0
  154. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_poller.py +0 -0
  155. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_registry.py +0 -0
  156. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_types.py +0 -0
  157. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_worker.py +0 -0
  158. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/dependency_links.txt +0 -0
  159. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/top_level.txt +0 -0
  160. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/scripts/fix_pyi_imports.py +0 -0
  161. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/scripts/generate_proto.py +0 -0
  162. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/setup.cfg +0 -0
  163. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/activity/test_activity_executor.py +0 -0
  164. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/activity/test_heartbeat.py +0 -0
  165. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/rpc/test_error.py +0 -0
  166. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/rpc/test_retry.py +0 -0
  167. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/test_fn_signature.py +0 -0
  168. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_activity_state_machine.py +0 -0
  169. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_timer_state_machine.py +0 -0
  170. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_context_retry_policy.py +0 -0
  171. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_decision_events_iterator.py +0 -0
  172. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_deterministic_event_loop.py +0 -0
  173. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_history_event_iterator.py +0 -0
  174. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_query_handling.py +0 -0
  175. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_retry_policy.py +0 -0
  176. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_signal_handling.py +0 -0
  177. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_waiter.py +0 -0
  178. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_workflow_engine.py +0 -0
  179. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/common_activities.py +0 -0
  180. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/data_converter_test.py +0 -0
  181. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/metrics/test_metrics.py +0 -0
  182. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/metrics/test_prometheus.py +0 -0
  183. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_activity.py +0 -0
  184. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_client.py +0 -0
  185. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_base_task_handler.py +0 -0
  186. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_task_handler_integration.py +0 -0
  187. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_worker_integration.py +0 -0
  188. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_poller.py +0 -0
  189. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_registry.py +0 -0
  190. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_worker.py +0 -0
  191. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/conftest.py +0 -0
  192. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/conftest.py +0 -0
  193. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/helper.py +0 -0
  194. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/nondeterminism/success.json +0 -0
  195. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/test_client.py +0 -0
  196. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_activities.py +0 -0
  197. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_continue_as_new.py +0 -0
  198. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_heartbeat.py +0 -0
  199. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_query.py +0 -0
  200. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_retry_policy.py +0 -0
  201. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_serialization.py +0 -0
  202. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_timer.py +0 -0
  203. {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_workflows.py +0 -0
@@ -32,7 +32,7 @@ pip install -e ".[dev]"
32
32
 
33
33
  ## Code Generation
34
34
  - Use `uv run python scripts/generate_proto.py` for protobuf generation
35
- - Use `uv run python scripts/dev.py` for development tasks
35
+ - Use `make` targets for development tasks
36
36
 
37
37
  ## Code Quality
38
38
  - **ALWAYS run linter and type checker after making code changes**
@@ -97,6 +97,6 @@ jobs:
97
97
  - name: Install dependencies
98
98
  run: |
99
99
  uv sync --extra dev
100
- - name: Run unit tests
100
+ - name: Run integration tests
101
101
  run: |
102
- uv run pytest -v --integration-tests
102
+ uv run pytest -v tests/integration_tests --integration-tests
@@ -33,11 +33,11 @@ test:
33
33
  # Run integration tests
34
34
  integration-test:
35
35
  @echo "Running integration tests..."
36
- uv run pytest -v --integration-tests
36
+ uv run pytest -v tests/integration_tests --integration-tests
37
37
 
38
38
  integration-test-keep:
39
39
  @echo "Running integration tests with cadence alive..."
40
- uv run pytest -v --integration-tests --keep-cadence-alive
40
+ uv run pytest -v tests/integration_tests --integration-tests --keep-cadence-alive
41
41
 
42
42
  # Clean generated files and caches
43
43
  clean:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cadence-python-client
3
- Version: 0.2.3
4
- Summary: Python framework for authoring Cadence workflows and activities
3
+ Version: 0.3.0
4
+ Summary: Python SDK for authoring Cadence workflows and activities
5
5
  Author: Cadence
6
6
  License: Apache-2.0
7
7
  Project-URL: Homepage, https://cadenceworkflow.io/
@@ -9,7 +9,7 @@ Project-URL: Documentation, https://cadenceworkflow.io/docs/get-started
9
9
  Project-URL: Repository, https://github.com/cadence-workflow/cadence-python-client
10
10
  Project-URL: Bug Tracker, https://github.com/cadence-workflow/cadence-python-client/issues
11
11
  Keywords: workflow,orchestration,distributed,async,cadence
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Operating System :: OS Independent
@@ -35,11 +35,7 @@ Requires-Dist: grpc-stubs>=1.53.0.6; extra == "dev"
35
35
  Requires-Dist: pytest>=8.4.1; extra == "dev"
36
36
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
37
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
38
- Requires-Dist: black>=23.0.0; extra == "dev"
39
- Requires-Dist: isort>=5.12.0; extra == "dev"
40
- Requires-Dist: flake8>=6.0.0; extra == "dev"
41
38
  Requires-Dist: mypy>=1.0.0; extra == "dev"
42
- Requires-Dist: pre-commit>=3.0.0; extra == "dev"
43
39
  Requires-Dist: pytest-docker>=3.2.3; extra == "dev"
44
40
  Requires-Dist: opentelemetry-instrumentation-grpc==0.60b1; extra == "dev"
45
41
  Requires-Dist: opentelemetry-sdk>=1.39.1; extra == "dev"
@@ -57,19 +53,13 @@ Requires-Dist: openai>=0.27.10; extra == "openai"
57
53
  Requires-Dist: openai-agents>=0.12.5; extra == "openai"
58
54
  Dynamic: license-file
59
55
 
60
- # Python framework for Cadence
56
+ # Cadence Python SDK
61
57
 
62
58
  [Cadence](https://github.com/uber/cadence) is a distributed, scalable, durable, and highly available orchestration engine we developed at Uber Engineering to execute asynchronous long-running business logic in a scalable and resilient way.
63
59
 
64
60
  If you'd like to propose a new feature, first join the [CNCF Slack workspace](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to start a discussion.
65
61
 
66
- `cadence-python-client` is the Python framework for authoring workflows and activities.
67
-
68
- ## Disclaimer
69
- **This SDK is currently an early work-in-progress (WIP) and is NOT ready for production use.**
70
-
71
- - This project is still in active development
72
- - APIs and interfaces are subject to change without notice
62
+ The Cadence Python SDK is the framework for authoring workflows and activities using the Python programming language.
73
63
 
74
64
  ## Installation
75
65
 
@@ -87,6 +77,8 @@ uv add cadence-python-client
87
77
 
88
78
  The core package supports Python `>=3.11,<3.14`.
89
79
 
80
+ The SDK is ready for production use using PYPI. To build from source, clone the repository and install the development dependencies.
81
+
90
82
  Clone the repository if you want to develop locally:
91
83
 
92
84
  ```bash
@@ -1,16 +1,10 @@
1
- # Python framework for Cadence
1
+ # Cadence Python SDK
2
2
 
3
3
  [Cadence](https://github.com/uber/cadence) is a distributed, scalable, durable, and highly available orchestration engine we developed at Uber Engineering to execute asynchronous long-running business logic in a scalable and resilient way.
4
4
 
5
5
  If you'd like to propose a new feature, first join the [CNCF Slack workspace](https://communityinviter.com/apps/cloud-native/cncf) in the **#cadence-users** channel to start a discussion.
6
6
 
7
- `cadence-python-client` is the Python framework for authoring workflows and activities.
8
-
9
- ## Disclaimer
10
- **This SDK is currently an early work-in-progress (WIP) and is NOT ready for production use.**
11
-
12
- - This project is still in active development
13
- - APIs and interfaces are subject to change without notice
7
+ The Cadence Python SDK is the framework for authoring workflows and activities using the Python programming language.
14
8
 
15
9
  ## Installation
16
10
 
@@ -28,6 +22,8 @@ uv add cadence-python-client
28
22
 
29
23
  The core package supports Python `>=3.11,<3.14`.
30
24
 
25
+ The SDK is ready for production use using PYPI. To build from source, clone the repository and install the development dependencies.
26
+
31
27
  Clone the repository if you want to develop locally:
32
28
 
33
29
  ```bash
@@ -0,0 +1,305 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ from asyncio import get_running_loop
5
+ from datetime import timedelta
6
+ from math import ceil
7
+ from typing import Iterator, Optional, Any, Unpack, Type, cast, Callable
8
+
9
+ from cadence._internal.workflow.deterministic_event_loop import DeterministicEventLoop
10
+ from cadence._internal.workflow.memo import memo_to_proto
11
+ from cadence._internal.workflow.retry_policy import retry_policy_to_proto
12
+ from cadence._internal.workflow.statemachine.decision_manager import DecisionManager
13
+ from cadence.api.v1 import workflow_pb2
14
+ from cadence.api.v1.common_pb2 import ActivityType, WorkflowType, WorkflowExecution
15
+ from cadence.api.v1.decision_pb2 import (
16
+ ScheduleActivityTaskDecisionAttributes,
17
+ SignalExternalWorkflowExecutionDecisionAttributes,
18
+ StartChildWorkflowExecutionDecisionAttributes,
19
+ StartTimerDecisionAttributes,
20
+ )
21
+ from cadence.api.v1.tasklist_pb2 import TaskList, TaskListKind
22
+ from cadence.data_converter import DataConverter
23
+ from cadence.workflow import (
24
+ ActivityOptions,
25
+ ChildWorkflowFuture,
26
+ ChildWorkflowOptions,
27
+ ResultType,
28
+ WorkflowContext,
29
+ WorkflowInfo,
30
+ )
31
+
32
+ _DEFAULT_ACTIVITY_OPTIONS: ActivityOptions = {
33
+ "schedule_to_close_timeout": timedelta(hours=1),
34
+ "schedule_to_start_timeout": timedelta(seconds=10),
35
+ }
36
+
37
+
38
+ class Context(WorkflowContext):
39
+ def __init__(
40
+ self,
41
+ info: WorkflowInfo,
42
+ decision_manager: DecisionManager,
43
+ ):
44
+ self._info = info
45
+ self._replay_mode = True
46
+ self._replay_current_time_milliseconds: Optional[int] = None
47
+ self._decision_manager = decision_manager
48
+
49
+ def info(self) -> WorkflowInfo:
50
+ return self._info
51
+
52
+ def data_converter(self) -> DataConverter:
53
+ return self.info().data_converter
54
+
55
+ async def execute_activity(
56
+ self,
57
+ activity: str,
58
+ result_type: Type[ResultType],
59
+ *args: Any,
60
+ **kwargs: Unpack[ActivityOptions],
61
+ ) -> ResultType:
62
+ opts: ActivityOptions = {**_DEFAULT_ACTIVITY_OPTIONS, **kwargs}
63
+ if "schedule_to_close_timeout" not in opts and (
64
+ "schedule_to_start_timeout" not in opts
65
+ or "start_to_close_timeout" not in opts
66
+ ):
67
+ raise ValueError(
68
+ "Either schedule_to_close_timeout or both schedule_to_start_timeout and start_to_close_timeout must be specified"
69
+ )
70
+
71
+ schedule_to_close = opts.get("schedule_to_close_timeout", None)
72
+ schedule_to_start = opts.get("schedule_to_start_timeout", None)
73
+ start_to_close = opts.get("start_to_close_timeout", None)
74
+ heartbeat = opts.get("heartbeat_timeout", None)
75
+
76
+ if schedule_to_close is None:
77
+ schedule_to_close = schedule_to_start + start_to_close # type: ignore
78
+
79
+ if start_to_close is None:
80
+ start_to_close = schedule_to_close
81
+
82
+ if schedule_to_start is None:
83
+ schedule_to_start = schedule_to_close
84
+
85
+ if heartbeat is None:
86
+ heartbeat = schedule_to_close
87
+
88
+ task_list = (
89
+ opts["task_list"]
90
+ if opts.get("task_list", None)
91
+ else self._info.workflow_task_list
92
+ )
93
+
94
+ activity_input = self.data_converter().to_data(list(args))
95
+ schedule_attributes = ScheduleActivityTaskDecisionAttributes(
96
+ activity_type=ActivityType(name=activity),
97
+ domain=self.info().workflow_domain,
98
+ task_list=TaskList(kind=TaskListKind.TASK_LIST_KIND_NORMAL, name=task_list),
99
+ input=activity_input,
100
+ schedule_to_close_timeout=_round_to_nearest_second(schedule_to_close),
101
+ schedule_to_start_timeout=_round_to_nearest_second(schedule_to_start),
102
+ start_to_close_timeout=_round_to_nearest_second(start_to_close),
103
+ heartbeat_timeout=_round_to_nearest_second(heartbeat),
104
+ retry_policy=retry_policy_to_proto(opts.get("retry_policy")),
105
+ header=None,
106
+ request_local_dispatch=False,
107
+ )
108
+
109
+ future = self._decision_manager.schedule_activity(schedule_attributes)
110
+ result_payload = await future
111
+
112
+ result = self.data_converter().from_data(result_payload, [result_type])[0]
113
+
114
+ return cast(ResultType, result)
115
+
116
+ async def execute_child_workflow(
117
+ self,
118
+ workflow_type: str,
119
+ result_type: Type[ResultType],
120
+ *args: Any,
121
+ **kwargs: Unpack[ChildWorkflowOptions],
122
+ ) -> ResultType:
123
+ future = await self.start_child_workflow(
124
+ workflow_type, result_type, *args, **kwargs
125
+ )
126
+ return await future
127
+
128
+ async def start_child_workflow(
129
+ self,
130
+ workflow_type: str,
131
+ result_type: Type[ResultType],
132
+ *args: Any,
133
+ **kwargs: Unpack[ChildWorkflowOptions],
134
+ ) -> ChildWorkflowFuture[ResultType]:
135
+ schedule_attributes = self._build_child_workflow_attrs(
136
+ workflow_type, *args, **kwargs
137
+ )
138
+ execution_future, result_future = (
139
+ self._decision_manager.schedule_child_workflow(
140
+ schedule_attributes,
141
+ parent_workflow_run_id=self._info.workflow_run_id,
142
+ )
143
+ )
144
+ workflow_execution = await execution_future
145
+ return ChildWorkflowFuture(
146
+ workflow_id=workflow_execution.workflow_id,
147
+ run_id=workflow_execution.run_id,
148
+ result_future=result_future,
149
+ result_type=result_type,
150
+ data_converter=self.data_converter(),
151
+ )
152
+
153
+ def _build_child_workflow_attrs(
154
+ self,
155
+ workflow_type: str,
156
+ *args: Any,
157
+ **kwargs: Unpack[ChildWorkflowOptions],
158
+ ) -> StartChildWorkflowExecutionDecisionAttributes:
159
+ execution_timeout = kwargs.get("execution_start_to_close_timeout")
160
+ if execution_timeout is None:
161
+ raise ValueError(
162
+ "execution_start_to_close_timeout is required for child workflow execution"
163
+ )
164
+ if execution_timeout <= timedelta(0):
165
+ raise ValueError("execution_start_to_close_timeout must be greater than 0")
166
+
167
+ task_timeout = kwargs.get("task_start_to_close_timeout", timedelta(seconds=10))
168
+ if task_timeout <= timedelta(0):
169
+ raise ValueError("task_start_to_close_timeout must be greater than 0")
170
+
171
+ domain = kwargs.get("domain") or self._info.workflow_domain
172
+ task_list = kwargs.get("task_list") or self._info.workflow_task_list
173
+
174
+ workflow_id = kwargs.get("workflow_id") or ""
175
+
176
+ parent_close_policy = kwargs.get(
177
+ "parent_close_policy",
178
+ workflow_pb2.PARENT_CLOSE_POLICY_TERMINATE,
179
+ )
180
+ workflow_id_reuse_policy = kwargs.get(
181
+ "workflow_id_reuse_policy",
182
+ workflow_pb2.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE_FAILED_ONLY,
183
+ )
184
+ if workflow_id_reuse_policy == workflow_pb2.WORKFLOW_ID_REUSE_POLICY_INVALID:
185
+ raise ValueError(
186
+ "workflow_id_reuse_policy cannot be WORKFLOW_ID_REUSE_POLICY_INVALID"
187
+ )
188
+
189
+ child_input = self.data_converter().to_data(list(args))
190
+ schedule_attributes = StartChildWorkflowExecutionDecisionAttributes(
191
+ domain=domain,
192
+ workflow_id=workflow_id,
193
+ workflow_type=WorkflowType(name=workflow_type),
194
+ task_list=TaskList(kind=TaskListKind.TASK_LIST_KIND_NORMAL, name=task_list),
195
+ input=child_input,
196
+ execution_start_to_close_timeout=_round_to_nearest_second(
197
+ execution_timeout
198
+ ),
199
+ task_start_to_close_timeout=_round_to_nearest_second(task_timeout),
200
+ parent_close_policy=parent_close_policy,
201
+ workflow_id_reuse_policy=workflow_id_reuse_policy,
202
+ retry_policy=retry_policy_to_proto(kwargs.get("retry_policy")),
203
+ )
204
+
205
+ cron_schedule = kwargs.get("cron_schedule")
206
+ if cron_schedule:
207
+ schedule_attributes.cron_schedule = cron_schedule
208
+
209
+ memo_proto = memo_to_proto(self.data_converter(), kwargs.get("memo"))
210
+ if memo_proto is not None:
211
+ schedule_attributes.memo.CopyFrom(memo_proto)
212
+
213
+ return schedule_attributes
214
+
215
+ async def signal_child_workflow(
216
+ self,
217
+ child_workflow_id: str,
218
+ signal_name: str,
219
+ *args: Any,
220
+ ) -> None:
221
+ if not child_workflow_id:
222
+ raise ValueError("child_workflow_id must not be empty")
223
+ await self._signal_workflow(
224
+ child_workflow_id, signal_name, args, child_workflow_only=True
225
+ )
226
+
227
+ async def signal_external_workflow(
228
+ self,
229
+ workflow_id: str,
230
+ signal_name: str,
231
+ *args: Any,
232
+ run_id: str = "",
233
+ domain: str = "",
234
+ ) -> None:
235
+ if not workflow_id:
236
+ raise ValueError("workflow_id must not be empty")
237
+ await self._signal_workflow(
238
+ workflow_id, signal_name, args, run_id=run_id, domain=domain
239
+ )
240
+
241
+ async def start_timer(self, duration: timedelta):
242
+ if duration.total_seconds() <= 0: # shortcut
243
+ return
244
+ future = self._decision_manager.start_timer(
245
+ StartTimerDecisionAttributes(
246
+ start_to_fire_timeout=duration,
247
+ )
248
+ )
249
+ await future
250
+
251
+ def set_replay_mode(self, replay: bool) -> None:
252
+ """Set whether the workflow is currently in replay mode."""
253
+ self._replay_mode = replay
254
+
255
+ def is_replay_mode(self) -> bool:
256
+ """Check if the workflow is currently in replay mode."""
257
+ return self._replay_mode
258
+
259
+ def set_replay_current_time_milliseconds(self, time_millis: int) -> None:
260
+ """Set the current replay time in milliseconds."""
261
+ self._replay_current_time_milliseconds = time_millis
262
+
263
+ def get_replay_current_time_milliseconds(self) -> Optional[int]:
264
+ """Get the current replay time in milliseconds."""
265
+ return self._replay_current_time_milliseconds
266
+
267
+ async def wait_condition(self, predicate: Callable[[], bool]) -> None:
268
+ loop = cast(DeterministicEventLoop, get_running_loop())
269
+ await loop.create_waiter(predicate)
270
+
271
+ @contextmanager
272
+ def _activate(self) -> Iterator["Context"]:
273
+ token = WorkflowContext._var.set(self)
274
+ try:
275
+ yield self
276
+ finally:
277
+ WorkflowContext._var.reset(token)
278
+
279
+ async def _signal_workflow(
280
+ self,
281
+ workflow_id: str,
282
+ signal_name: str,
283
+ args: tuple,
284
+ *,
285
+ run_id: str = "",
286
+ domain: str = "",
287
+ child_workflow_only: bool = False,
288
+ ) -> None:
289
+ if not signal_name:
290
+ raise ValueError("signal_name must not be empty")
291
+ attrs = SignalExternalWorkflowExecutionDecisionAttributes(
292
+ domain=domain or self._info.workflow_domain,
293
+ workflow_execution=WorkflowExecution(
294
+ workflow_id=workflow_id,
295
+ run_id=run_id,
296
+ ),
297
+ signal_name=signal_name,
298
+ input=self.data_converter().to_data(list(args)),
299
+ child_workflow_only=child_workflow_only,
300
+ )
301
+ await self._decision_manager.signal_external_workflow(attrs)
302
+
303
+
304
+ def _round_to_nearest_second(delta: timedelta) -> timedelta:
305
+ return timedelta(seconds=ceil(delta.total_seconds()))
@@ -0,0 +1,40 @@
1
+ """Convert user memo maps to/from protobuf :class:`cadence.api.v1.common_pb2.Memo`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Mapping
6
+
7
+ from cadence.api.v1 import common_pb2
8
+ from cadence.data_converter import DataConverter
9
+
10
+
11
+ def memo_to_proto(
12
+ data_converter: DataConverter,
13
+ memo: Mapping[str, Any] | None,
14
+ ) -> common_pb2.Memo | None:
15
+ """Serialize ``memo`` to protobuf, or ``None`` if no memo was provided.
16
+
17
+ Each value is encoded as a single-element list via the data converter,
18
+ matching Go/Java SDK behavior (one :class:`Payload` per key).
19
+ """
20
+ if not memo:
21
+ return None
22
+ out = common_pb2.Memo()
23
+ for key, value in memo.items():
24
+ out.fields[key].CopyFrom(data_converter.to_data([value]))
25
+ return out
26
+
27
+
28
+ def memo_from_proto(
29
+ data_converter: DataConverter,
30
+ memo: common_pb2.Memo,
31
+ ) -> dict[str, Any]:
32
+ """Deserialize a protobuf ``Memo`` back to a plain dict.
33
+
34
+ Each field payload was encoded as a single-element list; we unwrap that
35
+ first element to recover the original value.
36
+ """
37
+ return {
38
+ key: data_converter.from_data(payload, [None])[0]
39
+ for key, payload in memo.fields.items()
40
+ }
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+
3
+ from cadence._internal.workflow.statemachine.decision_state_machine import (
4
+ BaseDecisionStateMachine,
5
+ DecisionFuture,
6
+ DecisionId,
7
+ DecisionState,
8
+ DecisionType,
9
+ )
10
+ from cadence._internal.workflow.statemachine.event_dispatcher import EventDispatcher
11
+ from cadence._internal.workflow.statemachine.nondeterminism import (
12
+ record_immediate_cancel,
13
+ )
14
+ from cadence.api.v1 import decision, history
15
+ from cadence.api.v1.common_pb2 import Payload, WorkflowExecution
16
+ from cadence.error import (
17
+ ChildWorkflowExecutionCanceled,
18
+ ChildWorkflowExecutionFailed,
19
+ ChildWorkflowExecutionTerminated,
20
+ ChildWorkflowExecutionTimedOut,
21
+ StartChildWorkflowExecutionFailed,
22
+ )
23
+
24
+ child_workflow_events = EventDispatcher("initiated_event_id")
25
+
26
+
27
+ class ChildWorkflowExecutionStateMachine(BaseDecisionStateMachine):
28
+ """State machine for StartChildWorkflowExecution and child close events."""
29
+
30
+ request: decision.StartChildWorkflowExecutionDecisionAttributes
31
+ execution: DecisionFuture[WorkflowExecution]
32
+ result: DecisionFuture[Payload]
33
+ _run_id: str | None
34
+
35
+ def __init__(
36
+ self,
37
+ request: decision.StartChildWorkflowExecutionDecisionAttributes,
38
+ execution: DecisionFuture[WorkflowExecution],
39
+ result: DecisionFuture[Payload],
40
+ ) -> None:
41
+ super().__init__()
42
+ self.request = request
43
+ self.execution = execution
44
+ self.result = result
45
+ self._run_id = None
46
+
47
+ def get_id(self) -> DecisionId:
48
+ return DecisionId(DecisionType.CHILD_WORKFLOW, self.request.workflow_id)
49
+
50
+ def get_decision(self) -> decision.Decision | None:
51
+ match self.state:
52
+ case DecisionState.REQUESTED:
53
+ return decision.Decision(
54
+ start_child_workflow_execution_decision_attributes=self.request
55
+ )
56
+ case DecisionState.CANCELED_AFTER_REQUESTED:
57
+ return record_immediate_cancel(self.request)
58
+ case (
59
+ DecisionState.CANCELED_AFTER_RECORDED
60
+ | DecisionState.CANCELED_AFTER_STARTED
61
+ ):
62
+ return decision.Decision(
63
+ request_cancel_external_workflow_execution_decision_attributes=decision.RequestCancelExternalWorkflowExecutionDecisionAttributes(
64
+ domain=self.request.domain,
65
+ workflow_execution=WorkflowExecution(
66
+ workflow_id=self.request.workflow_id,
67
+ run_id=self._run_id or "",
68
+ ),
69
+ child_workflow_only=True,
70
+ )
71
+ )
72
+ case _:
73
+ return None
74
+
75
+ def request_cancel(self) -> bool:
76
+ match self.state:
77
+ case DecisionState.REQUESTED:
78
+ self._transition(DecisionState.CANCELED_AFTER_REQUESTED)
79
+ self.execution.force_cancel()
80
+ self.result.force_cancel()
81
+ return True
82
+ case DecisionState.RECORDED:
83
+ self._transition(DecisionState.CANCELED_AFTER_RECORDED)
84
+ self.execution.force_cancel()
85
+ return True
86
+ case DecisionState.STARTED:
87
+ self._transition(DecisionState.CANCELED_AFTER_STARTED)
88
+ return True
89
+ case _:
90
+ return False
91
+
92
+ @child_workflow_events.event("workflow_id", event_id_is_alias=True)
93
+ def handle_initiated(
94
+ self, _: history.StartChildWorkflowExecutionInitiatedEventAttributes
95
+ ) -> None:
96
+ self._transition(DecisionState.RECORDED)
97
+
98
+ @child_workflow_events.event()
99
+ def handle_initiation_failed(
100
+ self, event: history.StartChildWorkflowExecutionFailedEventAttributes
101
+ ) -> None:
102
+ self._transition(DecisionState.COMPLETED)
103
+ exc = StartChildWorkflowExecutionFailed(
104
+ f"start child failed: {event.cause}",
105
+ cause=event.cause,
106
+ workflow_id=event.workflow_id,
107
+ )
108
+ self.execution.set_exception(exc)
109
+ self.result.set_exception(exc)
110
+
111
+ @child_workflow_events.event()
112
+ def handle_started(
113
+ self, event: history.ChildWorkflowExecutionStartedEventAttributes
114
+ ) -> None:
115
+ self._run_id = event.workflow_execution.run_id
116
+ if self.state is DecisionState.CANCELED_AFTER_RECORDED:
117
+ self._transition(DecisionState.CANCELED_AFTER_STARTED)
118
+ else:
119
+ self._transition(DecisionState.STARTED)
120
+ if not self.execution.done():
121
+ self.execution.set_result(event.workflow_execution)
122
+
123
+ @child_workflow_events.event()
124
+ def handle_completed(
125
+ self, event: history.ChildWorkflowExecutionCompletedEventAttributes
126
+ ) -> None:
127
+ self._transition(DecisionState.COMPLETED)
128
+ self.result.set_result(event.result)
129
+
130
+ @child_workflow_events.event()
131
+ def handle_failed(
132
+ self, event: history.ChildWorkflowExecutionFailedEventAttributes
133
+ ) -> None:
134
+ self._transition(DecisionState.COMPLETED)
135
+ self.result.set_exception(
136
+ ChildWorkflowExecutionFailed(
137
+ event.failure.reason,
138
+ failure=event.failure,
139
+ )
140
+ )
141
+
142
+ @child_workflow_events.event()
143
+ def handle_canceled(
144
+ self, event: history.ChildWorkflowExecutionCanceledEventAttributes
145
+ ) -> None:
146
+ self._transition(DecisionState.COMPLETED)
147
+ self.result.set_exception(
148
+ ChildWorkflowExecutionCanceled(
149
+ "child workflow canceled", details=event.details
150
+ )
151
+ )
152
+
153
+ @child_workflow_events.event()
154
+ def handle_timed_out(
155
+ self, event: history.ChildWorkflowExecutionTimedOutEventAttributes
156
+ ) -> None:
157
+ self._transition(DecisionState.COMPLETED)
158
+ self.result.set_exception(
159
+ ChildWorkflowExecutionTimedOut(
160
+ f"child workflow timed out: {event.timeout_type}",
161
+ timeout_type=int(event.timeout_type),
162
+ )
163
+ )
164
+
165
+ @child_workflow_events.event()
166
+ def handle_terminated(
167
+ self, event: history.ChildWorkflowExecutionTerminatedEventAttributes
168
+ ) -> None:
169
+ self._transition(DecisionState.COMPLETED)
170
+ self.result.set_exception(ChildWorkflowExecutionTerminated())
171
+
172
+ @child_workflow_events.event(
173
+ "workflow_execution.workflow_id", event_id_is_alias=True
174
+ )
175
+ def handle_cancel_initiated(
176
+ self, _: history.RequestCancelExternalWorkflowExecutionInitiatedEventAttributes
177
+ ) -> None:
178
+ self._transition(DecisionState.CANCELLATION_RECORDED)
179
+
180
+ @child_workflow_events.event()
181
+ def handle_cancel_failed(
182
+ self, _: history.RequestCancelExternalWorkflowExecutionFailedEventAttributes
183
+ ) -> None:
184
+ self._transition(DecisionState.STARTED)