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.
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.cursorrules +1 -1
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/ci_checks.yml +2 -2
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/Makefile +2 -2
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/PKG-INFO +7 -15
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/README.md +4 -8
- cadence_python_client-0.3.0/cadence/_internal/workflow/context.py +305 -0
- cadence_python_client-0.3.0/cadence/_internal/workflow/memo.py +40 -0
- cadence_python_client-0.3.0/cadence/_internal/workflow/statemachine/child_workflow_execution_state_machine.py +184 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/decision_manager.py +78 -13
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/event_dispatcher.py +30 -5
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/nondeterminism.py +88 -0
- cadence_python_client-0.3.0/cadence/_internal/workflow/statemachine/signal_external_workflow_state_machine.py +74 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2.py +13 -31
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2.pyi +2 -31
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2.py +8 -8
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2.pyi +6 -2
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2.py +32 -32
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2.pyi +4 -2
- cadence_python_client-0.3.0/cadence/api/v1/tasklist_pb2.py +78 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/tasklist_pb2.pyi +4 -2
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/client.py +20 -14
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/error.py +39 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_decision_task_handler.py +21 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/workflow.py +136 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/PKG-INFO +7 -15
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/SOURCES.txt +9 -1
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/requires.txt +0 -4
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/pyproject.toml +2 -6
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_child_workflow_execution_state_machine.py +331 -0
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_decision_manager.py +535 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_nondeterminism.py +90 -0
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/statemachine/test_signal_external_workflow_state_machine.py +101 -0
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_context_child_workflow.py +452 -0
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_context_signal_child_workflow.py +256 -0
- cadence_python_client-0.3.0/tests/cadence/_internal/workflow/test_memo.py +53 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_client_workflow.py +26 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_schedule.py +37 -8
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_task_handler.py +32 -6
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_task_handler_integration.py +33 -5
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/docker-compose.yml +1 -2
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/nondeterminism/test_nondeterministic_workflows.py +8 -9
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/test_schedule.py +54 -36
- cadence_python_client-0.3.0/tests/integration_tests/workflow/test_child_workflow.py +231 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_signals.py +2 -1
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/uv.lock +3 -173
- cadence_python_client-0.2.3/cadence/_internal/workflow/context.py +0 -148
- cadence_python_client-0.2.3/cadence/api/v1/tasklist_pb2.py +0 -78
- cadence_python_client-0.2.3/scripts/dev.py +0 -187
- cadence_python_client-0.2.3/tests/cadence/_internal/workflow/statemachine/test_decision_manager.py +0 -190
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/dco.yml +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/pull_request_template.md +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/python-publish.yml +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.github/workflows/semantic-pr.yml +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.gitignore +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/.gitmodules +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/CONTRIBUTING.md +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/LICENSE +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/NOTICE +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_activity_executor.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_context.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_definition.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/activity/_heartbeat.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/fn_signature.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/error.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/retry.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/rpc/yarpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/active_cluster_selection_policy.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/decision_events_iterator.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/deterministic_event_loop.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/history_event_iterator.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/retry_policy.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/activity_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/cancellation.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/completion_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/decision_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/statemachine/timer_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/waiter.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/workflow_engine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/_internal/workflow/workflow_instance.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/activity.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/common_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/decision_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/domain_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/error_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/history_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/query_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/schedule_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_domain_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_meta_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_schedule_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_visibility_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_worker_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/service_workflow_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/tasklist_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/visibility_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2.pyi +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/api/v1/workflow_pb2_grpc.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/README.md +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_agent_runner.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_handoff.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_model.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_registry.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/cadence_tool.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/images/cadence-web-agent-run.jpg +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/openai_activities.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/contrib/openai/pydantic_data_converter.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/data_converter.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/constants.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/metrics.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/metrics/prometheus.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/query.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/client_example.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/grpc_usage_example.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/sample/simple_usage_example.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/signal.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/__init__.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_activity.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_base_task_handler.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_decision.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_poller.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_registry.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_types.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence/worker/_worker.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/dependency_links.txt +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/cadence_python_client.egg-info/top_level.txt +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/scripts/fix_pyi_imports.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/scripts/generate_proto.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/setup.cfg +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/activity/test_activity_executor.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/activity/test_heartbeat.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/rpc/test_error.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/rpc/test_retry.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/test_fn_signature.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_activity_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/statemachine/test_timer_state_machine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_context_retry_policy.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_decision_events_iterator.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_deterministic_event_loop.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_history_event_iterator.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_query_handling.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_retry_policy.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_signal_handling.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_waiter.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/_internal/workflow/test_workflow_engine.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/common_activities.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/data_converter_test.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/metrics/test_metrics.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/metrics/test_prometheus.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_activity.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/test_client.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_base_task_handler.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_task_handler_integration.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_decision_worker_integration.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_poller.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_registry.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/cadence/worker/test_worker.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/conftest.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/conftest.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/helper.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/nondeterminism/success.json +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/test_client.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_activities.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_continue_as_new.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_heartbeat.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_query.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_retry_policy.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_serialization.py +0 -0
- {cadence_python_client-0.2.3 → cadence_python_client-0.3.0}/tests/integration_tests/workflow/test_timer.py +0 -0
- {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 `
|
|
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**
|
|
@@ -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.
|
|
4
|
-
Summary: Python
|
|
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 ::
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|