hatchet-sdk 0.46.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
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.
- hatchet_sdk/__init__.py +25 -16
- hatchet_sdk/client.py +14 -39
- hatchet_sdk/clients/admin.py +203 -362
- hatchet_sdk/clients/dispatcher/action_listener.py +106 -84
- hatchet_sdk/clients/dispatcher/dispatcher.py +21 -21
- hatchet_sdk/clients/event_ts.py +23 -10
- hatchet_sdk/clients/events.py +96 -99
- hatchet_sdk/clients/rest/__init__.py +24 -0
- hatchet_sdk/clients/rest/api/__init__.py +2 -0
- hatchet_sdk/clients/rest/api/task_api.py +2174 -0
- hatchet_sdk/clients/rest/api/workflow_runs_api.py +638 -106
- hatchet_sdk/clients/rest/api_client.py +1 -1
- hatchet_sdk/clients/rest/configuration.py +8 -1
- hatchet_sdk/clients/rest/exceptions.py +21 -0
- hatchet_sdk/clients/rest/models/__init__.py +22 -0
- hatchet_sdk/clients/rest/models/tenant.py +4 -0
- hatchet_sdk/clients/rest/models/tenant_version.py +37 -0
- hatchet_sdk/clients/rest/models/update_tenant_request.py +7 -0
- hatchet_sdk/clients/rest/models/v1_cancel_task_request.py +104 -0
- hatchet_sdk/clients/rest/models/v1_dag_children.py +102 -0
- hatchet_sdk/clients/rest/models/v1_replay_task_request.py +104 -0
- hatchet_sdk/clients/rest/models/v1_task.py +174 -0
- hatchet_sdk/clients/rest/models/v1_task_event.py +118 -0
- hatchet_sdk/clients/rest/models/v1_task_event_list.py +110 -0
- hatchet_sdk/clients/rest/models/v1_task_event_type.py +55 -0
- hatchet_sdk/clients/rest/models/v1_task_filter.py +106 -0
- hatchet_sdk/clients/rest/models/v1_task_point_metric.py +92 -0
- hatchet_sdk/clients/rest/models/v1_task_point_metrics.py +100 -0
- hatchet_sdk/clients/rest/models/v1_task_run_metric.py +88 -0
- hatchet_sdk/clients/rest/models/v1_task_run_status.py +40 -0
- hatchet_sdk/clients/rest/models/v1_task_status.py +40 -0
- hatchet_sdk/clients/rest/models/v1_task_summary.py +212 -0
- hatchet_sdk/clients/rest/models/v1_task_summary_list.py +110 -0
- hatchet_sdk/clients/rest/models/v1_workflow_run.py +171 -0
- hatchet_sdk/clients/rest/models/v1_workflow_run_details.py +145 -0
- hatchet_sdk/clients/rest/models/v1_workflow_type.py +37 -0
- hatchet_sdk/clients/rest/models/workflow_run_shape_item_for_workflow_run_details.py +99 -0
- hatchet_sdk/clients/rest/rest.py +37 -26
- hatchet_sdk/clients/rest/tenacity_utils.py +1 -1
- hatchet_sdk/clients/rest_client.py +141 -116
- hatchet_sdk/clients/run_event_listener.py +66 -60
- hatchet_sdk/clients/workflow_listener.py +77 -64
- hatchet_sdk/config.py +117 -0
- hatchet_sdk/connection.py +27 -13
- hatchet_sdk/context/__init__.py +0 -1
- hatchet_sdk/context/context.py +143 -202
- hatchet_sdk/features/cron.py +43 -57
- hatchet_sdk/features/scheduled.py +60 -74
- hatchet_sdk/hatchet.py +192 -195
- hatchet_sdk/labels.py +4 -6
- hatchet_sdk/metadata.py +1 -1
- hatchet_sdk/opentelemetry/instrumentor.py +112 -35
- hatchet_sdk/rate_limit.py +9 -18
- hatchet_sdk/token.py +13 -9
- hatchet_sdk/utils/aio_utils.py +0 -40
- hatchet_sdk/utils/proto_enums.py +54 -0
- hatchet_sdk/utils/typing.py +9 -1
- hatchet_sdk/v0/__init__.py +251 -0
- hatchet_sdk/v0/client.py +119 -0
- hatchet_sdk/v0/clients/admin.py +541 -0
- hatchet_sdk/v0/clients/dispatcher/action_listener.py +422 -0
- hatchet_sdk/v0/clients/dispatcher/dispatcher.py +204 -0
- hatchet_sdk/v0/clients/event_ts.py +28 -0
- hatchet_sdk/v0/clients/events.py +182 -0
- hatchet_sdk/v0/clients/rest/__init__.py +307 -0
- hatchet_sdk/v0/clients/rest/api/__init__.py +19 -0
- hatchet_sdk/v0/clients/rest/api/api_token_api.py +858 -0
- hatchet_sdk/v0/clients/rest/api/default_api.py +2259 -0
- hatchet_sdk/v0/clients/rest/api/event_api.py +2548 -0
- hatchet_sdk/v0/clients/rest/api/github_api.py +331 -0
- hatchet_sdk/v0/clients/rest/api/healthcheck_api.py +483 -0
- hatchet_sdk/v0/clients/rest/api/log_api.py +449 -0
- hatchet_sdk/v0/clients/rest/api/metadata_api.py +728 -0
- hatchet_sdk/v0/clients/rest/api/rate_limits_api.py +423 -0
- hatchet_sdk/v0/clients/rest/api/slack_api.py +577 -0
- hatchet_sdk/v0/clients/rest/api/sns_api.py +872 -0
- hatchet_sdk/v0/clients/rest/api/step_run_api.py +2202 -0
- hatchet_sdk/v0/clients/rest/api/tenant_api.py +4430 -0
- hatchet_sdk/v0/clients/rest/api/user_api.py +2888 -0
- hatchet_sdk/v0/clients/rest/api/worker_api.py +858 -0
- hatchet_sdk/v0/clients/rest/api/workflow_api.py +6312 -0
- hatchet_sdk/v0/clients/rest/api/workflow_run_api.py +1932 -0
- hatchet_sdk/v0/clients/rest/api/workflow_runs_api.py +610 -0
- hatchet_sdk/v0/clients/rest/api_client.py +759 -0
- hatchet_sdk/v0/clients/rest/api_response.py +22 -0
- hatchet_sdk/v0/clients/rest/configuration.py +611 -0
- hatchet_sdk/v0/clients/rest/exceptions.py +200 -0
- hatchet_sdk/v0/clients/rest/models/__init__.py +274 -0
- hatchet_sdk/v0/clients/rest/models/accept_invite_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/api_error.py +102 -0
- hatchet_sdk/v0/clients/rest/models/api_errors.py +100 -0
- hatchet_sdk/v0/clients/rest/models/api_meta.py +144 -0
- hatchet_sdk/v0/clients/rest/models/api_meta_auth.py +85 -0
- hatchet_sdk/v0/clients/rest/models/api_meta_integration.py +88 -0
- hatchet_sdk/v0/clients/rest/models/api_meta_posthog.py +90 -0
- hatchet_sdk/v0/clients/rest/models/api_resource_meta.py +98 -0
- hatchet_sdk/v0/clients/rest/models/api_token.py +105 -0
- hatchet_sdk/v0/clients/rest/models/bulk_create_event_request.py +100 -0
- hatchet_sdk/v0/clients/rest/models/bulk_create_event_response.py +110 -0
- hatchet_sdk/v0/clients/rest/models/cancel_event_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/cancel_step_run_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/concurrency_limit_strategy.py +39 -0
- hatchet_sdk/v0/clients/rest/models/create_api_token_request.py +92 -0
- hatchet_sdk/v0/clients/rest/models/create_api_token_response.py +83 -0
- hatchet_sdk/v0/clients/rest/models/create_cron_workflow_trigger_request.py +98 -0
- hatchet_sdk/v0/clients/rest/models/create_event_request.py +95 -0
- hatchet_sdk/v0/clients/rest/models/create_pull_request_from_step_run.py +83 -0
- hatchet_sdk/v0/clients/rest/models/create_sns_integration_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/create_tenant_alert_email_group_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/create_tenant_invite_request.py +86 -0
- hatchet_sdk/v0/clients/rest/models/create_tenant_request.py +84 -0
- hatchet_sdk/v0/clients/rest/models/cron_workflows.py +131 -0
- hatchet_sdk/v0/clients/rest/models/cron_workflows_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/cron_workflows_method.py +37 -0
- hatchet_sdk/v0/clients/rest/models/cron_workflows_order_by_field.py +37 -0
- hatchet_sdk/v0/clients/rest/models/event.py +143 -0
- hatchet_sdk/v0/clients/rest/models/event_data.py +83 -0
- hatchet_sdk/v0/clients/rest/models/event_key_list.py +98 -0
- hatchet_sdk/v0/clients/rest/models/event_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/event_order_by_direction.py +37 -0
- hatchet_sdk/v0/clients/rest/models/event_order_by_field.py +36 -0
- hatchet_sdk/v0/clients/rest/models/event_update_cancel200_response.py +85 -0
- hatchet_sdk/v0/clients/rest/models/event_workflow_run_summary.py +116 -0
- hatchet_sdk/v0/clients/rest/models/events.py +110 -0
- hatchet_sdk/v0/clients/rest/models/get_step_run_diff_response.py +100 -0
- hatchet_sdk/v0/clients/rest/models/github_app_installation.py +107 -0
- hatchet_sdk/v0/clients/rest/models/github_branch.py +86 -0
- hatchet_sdk/v0/clients/rest/models/github_repo.py +86 -0
- hatchet_sdk/v0/clients/rest/models/info_get_version200_response.py +83 -0
- hatchet_sdk/v0/clients/rest/models/job.py +132 -0
- hatchet_sdk/v0/clients/rest/models/job_run.py +176 -0
- hatchet_sdk/v0/clients/rest/models/job_run_status.py +41 -0
- hatchet_sdk/v0/clients/rest/models/link_github_repository_request.py +106 -0
- hatchet_sdk/v0/clients/rest/models/list_api_tokens_response.py +110 -0
- hatchet_sdk/v0/clients/rest/models/list_github_app_installations_response.py +112 -0
- hatchet_sdk/v0/clients/rest/models/list_pull_requests_response.py +100 -0
- hatchet_sdk/v0/clients/rest/models/list_slack_webhooks.py +110 -0
- hatchet_sdk/v0/clients/rest/models/list_sns_integrations.py +110 -0
- hatchet_sdk/v0/clients/rest/models/log_line.py +94 -0
- hatchet_sdk/v0/clients/rest/models/log_line_level.py +39 -0
- hatchet_sdk/v0/clients/rest/models/log_line_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/log_line_order_by_direction.py +37 -0
- hatchet_sdk/v0/clients/rest/models/log_line_order_by_field.py +36 -0
- hatchet_sdk/v0/clients/rest/models/pagination_response.py +95 -0
- hatchet_sdk/v0/clients/rest/models/pull_request.py +112 -0
- hatchet_sdk/v0/clients/rest/models/pull_request_state.py +37 -0
- hatchet_sdk/v0/clients/rest/models/queue_metrics.py +97 -0
- hatchet_sdk/v0/clients/rest/models/rate_limit.py +117 -0
- hatchet_sdk/v0/clients/rest/models/rate_limit_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_direction.py +37 -0
- hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_field.py +38 -0
- hatchet_sdk/v0/clients/rest/models/recent_step_runs.py +118 -0
- hatchet_sdk/v0/clients/rest/models/reject_invite_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/replay_event_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_response.py +100 -0
- hatchet_sdk/v0/clients/rest/models/rerun_step_run_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/schedule_workflow_run_request.py +92 -0
- hatchet_sdk/v0/clients/rest/models/scheduled_run_status.py +42 -0
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows.py +149 -0
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_method.py +37 -0
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_order_by_field.py +37 -0
- hatchet_sdk/v0/clients/rest/models/semaphore_slots.py +113 -0
- hatchet_sdk/v0/clients/rest/models/slack_webhook.py +127 -0
- hatchet_sdk/v0/clients/rest/models/sns_integration.py +114 -0
- hatchet_sdk/v0/clients/rest/models/step.py +123 -0
- hatchet_sdk/v0/clients/rest/models/step_run.py +202 -0
- hatchet_sdk/v0/clients/rest/models/step_run_archive.py +142 -0
- hatchet_sdk/v0/clients/rest/models/step_run_archive_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/step_run_diff.py +91 -0
- hatchet_sdk/v0/clients/rest/models/step_run_event.py +122 -0
- hatchet_sdk/v0/clients/rest/models/step_run_event_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/step_run_event_reason.py +52 -0
- hatchet_sdk/v0/clients/rest/models/step_run_event_severity.py +38 -0
- hatchet_sdk/v0/clients/rest/models/step_run_status.py +44 -0
- hatchet_sdk/v0/clients/rest/models/tenant.py +118 -0
- hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group.py +98 -0
- hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group_list.py +112 -0
- hatchet_sdk/v0/clients/rest/models/tenant_alerting_settings.py +143 -0
- hatchet_sdk/v0/clients/rest/models/tenant_invite.py +120 -0
- hatchet_sdk/v0/clients/rest/models/tenant_invite_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/tenant_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/tenant_member.py +123 -0
- hatchet_sdk/v0/clients/rest/models/tenant_member_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/tenant_member_role.py +38 -0
- hatchet_sdk/v0/clients/rest/models/tenant_queue_metrics.py +116 -0
- hatchet_sdk/v0/clients/rest/models/tenant_resource.py +40 -0
- hatchet_sdk/v0/clients/rest/models/tenant_resource_limit.py +135 -0
- hatchet_sdk/v0/clients/rest/models/tenant_resource_policy.py +102 -0
- hatchet_sdk/v0/clients/rest/models/tenant_step_run_queue_metrics.py +83 -0
- hatchet_sdk/v0/clients/rest/models/trigger_workflow_run_request.py +91 -0
- hatchet_sdk/v0/clients/rest/models/update_tenant_alert_email_group_request.py +83 -0
- hatchet_sdk/v0/clients/rest/models/update_tenant_invite_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/update_tenant_request.py +137 -0
- hatchet_sdk/v0/clients/rest/models/update_worker_request.py +87 -0
- hatchet_sdk/v0/clients/rest/models/user.py +126 -0
- hatchet_sdk/v0/clients/rest/models/user_change_password_request.py +88 -0
- hatchet_sdk/v0/clients/rest/models/user_login_request.py +86 -0
- hatchet_sdk/v0/clients/rest/models/user_register_request.py +91 -0
- hatchet_sdk/v0/clients/rest/models/user_tenant_memberships_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/user_tenant_public.py +86 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker.py +100 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_create_request.py +94 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_create_response.py +98 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_created.py +102 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_list_response.py +110 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request.py +102 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request_list_response.py +104 -0
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request_method.py +38 -0
- hatchet_sdk/v0/clients/rest/models/worker.py +239 -0
- hatchet_sdk/v0/clients/rest/models/worker_label.py +102 -0
- hatchet_sdk/v0/clients/rest/models/worker_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/worker_runtime_info.py +103 -0
- hatchet_sdk/v0/clients/rest/models/worker_runtime_sdks.py +38 -0
- hatchet_sdk/v0/clients/rest/models/worker_type.py +38 -0
- hatchet_sdk/v0/clients/rest/models/workflow.py +165 -0
- hatchet_sdk/v0/clients/rest/models/workflow_concurrency.py +107 -0
- hatchet_sdk/v0/clients/rest/models/workflow_deployment_config.py +136 -0
- hatchet_sdk/v0/clients/rest/models/workflow_kind.py +38 -0
- hatchet_sdk/v0/clients/rest/models/workflow_list.py +120 -0
- hatchet_sdk/v0/clients/rest/models/workflow_metrics.py +97 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run.py +188 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_cancel200_response.py +85 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_list.py +110 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_direction.py +37 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_field.py +39 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_shape.py +186 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_status.py +42 -0
- hatchet_sdk/v0/clients/rest/models/workflow_run_triggered_by.py +112 -0
- hatchet_sdk/v0/clients/rest/models/workflow_runs_cancel_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics.py +94 -0
- hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics_counts.py +104 -0
- hatchet_sdk/v0/clients/rest/models/workflow_tag.py +84 -0
- hatchet_sdk/v0/clients/rest/models/workflow_trigger_cron_ref.py +86 -0
- hatchet_sdk/v0/clients/rest/models/workflow_trigger_event_ref.py +86 -0
- hatchet_sdk/v0/clients/rest/models/workflow_triggers.py +141 -0
- hatchet_sdk/v0/clients/rest/models/workflow_update_request.py +85 -0
- hatchet_sdk/v0/clients/rest/models/workflow_version.py +170 -0
- hatchet_sdk/v0/clients/rest/models/workflow_version_concurrency.py +114 -0
- hatchet_sdk/v0/clients/rest/models/workflow_version_definition.py +85 -0
- hatchet_sdk/v0/clients/rest/models/workflow_version_meta.py +123 -0
- hatchet_sdk/v0/clients/rest/models/workflow_workers_count.py +95 -0
- hatchet_sdk/v0/clients/rest/rest.py +187 -0
- hatchet_sdk/v0/clients/rest/tenacity_utils.py +39 -0
- hatchet_sdk/v0/clients/rest_client.py +613 -0
- hatchet_sdk/v0/clients/run_event_listener.py +260 -0
- hatchet_sdk/v0/clients/workflow_listener.py +277 -0
- hatchet_sdk/v0/connection.py +63 -0
- hatchet_sdk/v0/context/__init__.py +1 -0
- hatchet_sdk/v0/context/context.py +446 -0
- hatchet_sdk/v0/context/worker_context.py +28 -0
- hatchet_sdk/v0/contracts/dispatcher_pb2.py +102 -0
- hatchet_sdk/v0/contracts/dispatcher_pb2.pyi +387 -0
- hatchet_sdk/v0/contracts/dispatcher_pb2_grpc.py +621 -0
- hatchet_sdk/v0/contracts/events_pb2.py +46 -0
- hatchet_sdk/v0/contracts/events_pb2.pyi +87 -0
- hatchet_sdk/v0/contracts/events_pb2_grpc.py +274 -0
- hatchet_sdk/v0/contracts/workflows_pb2.py +80 -0
- hatchet_sdk/v0/contracts/workflows_pb2.pyi +312 -0
- hatchet_sdk/v0/contracts/workflows_pb2_grpc.py +277 -0
- hatchet_sdk/v0/features/cron.py +286 -0
- hatchet_sdk/v0/features/scheduled.py +248 -0
- hatchet_sdk/v0/hatchet.py +310 -0
- hatchet_sdk/v0/labels.py +10 -0
- hatchet_sdk/{loader.py → v0/loader.py} +11 -0
- hatchet_sdk/v0/logger.py +13 -0
- hatchet_sdk/v0/metadata.py +2 -0
- hatchet_sdk/v0/opentelemetry/instrumentor.py +396 -0
- hatchet_sdk/v0/rate_limit.py +126 -0
- hatchet_sdk/v0/semver.py +30 -0
- hatchet_sdk/v0/token.py +27 -0
- hatchet_sdk/v0/utils/aio_utils.py +137 -0
- hatchet_sdk/v0/utils/backoff.py +9 -0
- hatchet_sdk/v0/utils/typing.py +12 -0
- hatchet_sdk/{v2 → v0/v2}/callable.py +8 -8
- hatchet_sdk/{v2 → v0/v2}/concurrency.py +2 -2
- hatchet_sdk/{v2 → v0/v2}/hatchet.py +10 -10
- hatchet_sdk/v0/worker/__init__.py +1 -0
- hatchet_sdk/v0/worker/action_listener_process.py +278 -0
- hatchet_sdk/v0/worker/runner/run_loop_manager.py +112 -0
- hatchet_sdk/v0/worker/runner/runner.py +460 -0
- hatchet_sdk/v0/worker/runner/utils/capture_logs.py +81 -0
- hatchet_sdk/v0/worker/runner/utils/error_with_traceback.py +6 -0
- hatchet_sdk/v0/worker/worker.py +391 -0
- hatchet_sdk/v0/workflow.py +261 -0
- hatchet_sdk/v0/workflow_run.py +59 -0
- hatchet_sdk/worker/__init__.py +0 -1
- hatchet_sdk/worker/action_listener_process.py +36 -33
- hatchet_sdk/worker/runner/run_loop_manager.py +18 -16
- hatchet_sdk/worker/runner/runner.py +37 -59
- hatchet_sdk/worker/runner/utils/capture_logs.py +25 -14
- hatchet_sdk/worker/runner/utils/error_with_traceback.py +1 -1
- hatchet_sdk/worker/worker.py +61 -75
- hatchet_sdk/workflow.py +473 -207
- hatchet_sdk/workflow_run.py +14 -25
- {hatchet_sdk-0.46.1.dist-info → hatchet_sdk-1.0.0.dist-info}/METADATA +3 -2
- hatchet_sdk-1.0.0.dist-info/RECORD +485 -0
- {hatchet_sdk-0.46.1.dist-info → hatchet_sdk-1.0.0.dist-info}/entry_points.txt +1 -0
- hatchet_sdk/utils/serialization.py +0 -18
- hatchet_sdk-0.46.1.dist-info/RECORD +0 -237
- /hatchet_sdk/{utils → v0/utils}/types.py +0 -0
- {hatchet_sdk-0.46.1.dist-info → hatchet_sdk-1.0.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import inspect
|
|
3
|
+
from concurrent.futures import Executor
|
|
4
|
+
from functools import partial, wraps
|
|
5
|
+
from threading import Thread
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## TODO: Stricter typing here
|
|
10
|
+
def sync_to_async(func: Any) -> Any:
|
|
11
|
+
"""
|
|
12
|
+
A decorator to run a synchronous function or coroutine in an asynchronous context with added
|
|
13
|
+
asyncio loop safety.
|
|
14
|
+
|
|
15
|
+
This decorator allows you to safely call synchronous functions or coroutines from an
|
|
16
|
+
asynchronous function by running them in an executor.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
func (callable): The synchronous function or coroutine to be run asynchronously.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
callable: An asynchronous wrapper function that runs the given function in an executor.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
@sync_to_async
|
|
26
|
+
def sync_function(x, y):
|
|
27
|
+
return x + y
|
|
28
|
+
|
|
29
|
+
@sync_to_async
|
|
30
|
+
async def async_function(x, y):
|
|
31
|
+
return x + y
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def undecorated_function(x, y):
|
|
35
|
+
return x + y
|
|
36
|
+
|
|
37
|
+
async def main():
|
|
38
|
+
result1 = await sync_function(1, 2)
|
|
39
|
+
result2 = await async_function(3, 4)
|
|
40
|
+
result3 = await sync_to_async(undecorated_function)(5, 6)
|
|
41
|
+
print(result1, result2, result3)
|
|
42
|
+
|
|
43
|
+
asyncio.run(main())
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
## TODO: Stricter typing here
|
|
47
|
+
@wraps(func)
|
|
48
|
+
async def run(
|
|
49
|
+
*args: Any,
|
|
50
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
51
|
+
executor: Executor | None = None,
|
|
52
|
+
**kwargs: Any
|
|
53
|
+
) -> Any:
|
|
54
|
+
"""
|
|
55
|
+
The asynchronous wrapper function that runs the given function in an executor.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
*args: Positional arguments to pass to the function.
|
|
59
|
+
loop (asyncio.AbstractEventLoop, optional): The event loop to use. If None, the current running loop is used.
|
|
60
|
+
executor (concurrent.futures.Executor, optional): The executor to use. If None, the default executor is used.
|
|
61
|
+
**kwargs: Keyword arguments to pass to the function.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The result of the function call.
|
|
65
|
+
"""
|
|
66
|
+
if loop is None:
|
|
67
|
+
loop = asyncio.get_running_loop()
|
|
68
|
+
|
|
69
|
+
if inspect.iscoroutinefunction(func):
|
|
70
|
+
# Wrap the coroutine to run it in an executor
|
|
71
|
+
async def wrapper() -> Any:
|
|
72
|
+
return await func(*args, **kwargs)
|
|
73
|
+
|
|
74
|
+
pfunc = partial(asyncio.run, wrapper())
|
|
75
|
+
return await loop.run_in_executor(executor, pfunc)
|
|
76
|
+
else:
|
|
77
|
+
# Run the synchronous function in an executor
|
|
78
|
+
pfunc = partial(func, *args, **kwargs)
|
|
79
|
+
return await loop.run_in_executor(executor, pfunc)
|
|
80
|
+
|
|
81
|
+
return run
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class EventLoopThread:
|
|
85
|
+
"""A class that manages an asyncio event loop running in a separate thread."""
|
|
86
|
+
|
|
87
|
+
def __init__(self) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Initializes the EventLoopThread by creating an event loop
|
|
90
|
+
and setting up a thread to run the loop.
|
|
91
|
+
"""
|
|
92
|
+
self.loop = asyncio.new_event_loop()
|
|
93
|
+
self.thread = Thread(target=self.run_loop_in_thread, args=(self.loop,))
|
|
94
|
+
|
|
95
|
+
def __enter__(self, *a, **kw) -> asyncio.AbstractEventLoop:
|
|
96
|
+
"""
|
|
97
|
+
Starts the thread running the event loop when entering the context.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
asyncio.AbstractEventLoop: The event loop running in the separate thread.
|
|
101
|
+
"""
|
|
102
|
+
self.thread.start()
|
|
103
|
+
return self.loop
|
|
104
|
+
|
|
105
|
+
def __exit__(self, *a, **kw) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Stops the event loop and joins the thread when exiting the context.
|
|
108
|
+
"""
|
|
109
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
110
|
+
self.thread.join()
|
|
111
|
+
|
|
112
|
+
def run_loop_in_thread(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Sets the event loop for the current thread and runs it forever.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
loop (asyncio.AbstractEventLoop): The event loop to run.
|
|
118
|
+
"""
|
|
119
|
+
asyncio.set_event_loop(loop)
|
|
120
|
+
loop.run_forever()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_active_event_loop() -> asyncio.AbstractEventLoop | None:
|
|
124
|
+
"""
|
|
125
|
+
Get the active event loop.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
asyncio.AbstractEventLoop: The active event loop, or None if there is no active
|
|
129
|
+
event loop in the current thread.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
return asyncio.get_event_loop()
|
|
133
|
+
except RuntimeError as e:
|
|
134
|
+
if str(e).startswith("There is no current event loop in thread"):
|
|
135
|
+
return None
|
|
136
|
+
else:
|
|
137
|
+
raise e
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
async def exp_backoff_sleep(attempt: int, max_sleep_time: float = 5) -> None:
|
|
6
|
+
base_time = 0.1 # starting sleep time in seconds (100 milliseconds)
|
|
7
|
+
jitter = random.uniform(0, base_time) # add random jitter
|
|
8
|
+
sleep_time = min(base_time * (2**attempt) + jitter, max_sleep_time)
|
|
9
|
+
await asyncio.sleep(sleep_time)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Any, Type, TypeGuard, TypeVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
T = TypeVar("T", bound=BaseModel)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_basemodel_subclass(model: Any) -> bool:
|
|
9
|
+
try:
|
|
10
|
+
return issubclass(model, BaseModel)
|
|
11
|
+
except TypeError:
|
|
12
|
+
return False
|
|
@@ -11,9 +11,9 @@ from typing import (
|
|
|
11
11
|
Union,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from hatchet_sdk.clients.admin import ChildTriggerWorkflowOptions
|
|
15
|
-
from hatchet_sdk.context.context import Context
|
|
16
|
-
from hatchet_sdk.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
14
|
+
from hatchet_sdk.v0.clients.admin import ChildTriggerWorkflowOptions
|
|
15
|
+
from hatchet_sdk.v0.context.context import Context
|
|
16
|
+
from hatchet_sdk.v0.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
17
17
|
CreateStepRateLimit,
|
|
18
18
|
CreateWorkflowJobOpts,
|
|
19
19
|
CreateWorkflowStepOpts,
|
|
@@ -23,11 +23,11 @@ from hatchet_sdk.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
|
23
23
|
WorkflowConcurrencyOpts,
|
|
24
24
|
WorkflowKind,
|
|
25
25
|
)
|
|
26
|
-
from hatchet_sdk.labels import DesiredWorkerLabel
|
|
27
|
-
from hatchet_sdk.logger import logger
|
|
28
|
-
from hatchet_sdk.rate_limit import RateLimit
|
|
29
|
-
from hatchet_sdk.v2.concurrency import ConcurrencyFunction
|
|
30
|
-
from hatchet_sdk.workflow_run import RunRef
|
|
26
|
+
from hatchet_sdk.v0.labels import DesiredWorkerLabel
|
|
27
|
+
from hatchet_sdk.v0.logger import logger
|
|
28
|
+
from hatchet_sdk.v0.rate_limit import RateLimit
|
|
29
|
+
from hatchet_sdk.v0.v2.concurrency import ConcurrencyFunction
|
|
30
|
+
from hatchet_sdk.v0.workflow_run import RunRef
|
|
31
31
|
|
|
32
32
|
T = TypeVar("T")
|
|
33
33
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any, Callable
|
|
2
2
|
|
|
3
|
-
from hatchet_sdk.context.context import Context
|
|
4
|
-
from hatchet_sdk.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
3
|
+
from hatchet_sdk.v0.context.context import Context
|
|
4
|
+
from hatchet_sdk.v0.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
5
5
|
ConcurrencyLimitStrategy,
|
|
6
6
|
)
|
|
7
7
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
from typing import Any, Callable, TypeVar, Union
|
|
2
2
|
|
|
3
|
-
from hatchet_sdk import Worker
|
|
4
|
-
from hatchet_sdk.context.context import Context
|
|
5
|
-
from hatchet_sdk.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
3
|
+
from hatchet_sdk.v0 import Worker
|
|
4
|
+
from hatchet_sdk.v0.context.context import Context
|
|
5
|
+
from hatchet_sdk.v0.contracts.workflows_pb2 import ( # type: ignore[attr-defined]
|
|
6
6
|
ConcurrencyLimitStrategy,
|
|
7
7
|
StickyStrategy,
|
|
8
8
|
)
|
|
9
|
-
from hatchet_sdk.hatchet import Hatchet as HatchetV1
|
|
10
|
-
from hatchet_sdk.hatchet import workflow
|
|
11
|
-
from hatchet_sdk.labels import DesiredWorkerLabel
|
|
12
|
-
from hatchet_sdk.rate_limit import RateLimit
|
|
13
|
-
from hatchet_sdk.v2.callable import DurableContext, HatchetCallable
|
|
14
|
-
from hatchet_sdk.v2.concurrency import ConcurrencyFunction
|
|
15
|
-
from hatchet_sdk.worker.worker import register_on_worker
|
|
9
|
+
from hatchet_sdk.v0.hatchet import Hatchet as HatchetV1
|
|
10
|
+
from hatchet_sdk.v0.hatchet import workflow
|
|
11
|
+
from hatchet_sdk.v0.labels import DesiredWorkerLabel
|
|
12
|
+
from hatchet_sdk.v0.rate_limit import RateLimit
|
|
13
|
+
from hatchet_sdk.v0.v2.callable import DurableContext, HatchetCallable
|
|
14
|
+
from hatchet_sdk.v0.v2.concurrency import ConcurrencyFunction
|
|
15
|
+
from hatchet_sdk.v0.worker.worker import register_on_worker
|
|
16
16
|
|
|
17
17
|
T = TypeVar("T")
|
|
18
18
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .worker import Worker, WorkerStartOptions, WorkerStatus
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import signal
|
|
4
|
+
import time
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from multiprocessing import Queue
|
|
7
|
+
from typing import Any, List, Mapping, Optional
|
|
8
|
+
|
|
9
|
+
import grpc
|
|
10
|
+
|
|
11
|
+
from hatchet_sdk.v0.clients.dispatcher.action_listener import Action
|
|
12
|
+
from hatchet_sdk.v0.clients.dispatcher.dispatcher import (
|
|
13
|
+
ActionListener,
|
|
14
|
+
GetActionListenerRequest,
|
|
15
|
+
new_dispatcher,
|
|
16
|
+
)
|
|
17
|
+
from hatchet_sdk.v0.contracts.dispatcher_pb2 import (
|
|
18
|
+
GROUP_KEY_EVENT_TYPE_STARTED,
|
|
19
|
+
STEP_EVENT_TYPE_STARTED,
|
|
20
|
+
ActionType,
|
|
21
|
+
)
|
|
22
|
+
from hatchet_sdk.v0.loader import ClientConfig
|
|
23
|
+
from hatchet_sdk.v0.logger import logger
|
|
24
|
+
from hatchet_sdk.v0.utils.backoff import exp_backoff_sleep
|
|
25
|
+
|
|
26
|
+
ACTION_EVENT_RETRY_COUNT = 5
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ActionEvent:
|
|
31
|
+
action: Action
|
|
32
|
+
type: Any # TODO type
|
|
33
|
+
payload: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
STOP_LOOP = "STOP_LOOP" # Sentinel object to stop the loop
|
|
37
|
+
|
|
38
|
+
# TODO link to a block post
|
|
39
|
+
BLOCKED_THREAD_WARNING = (
|
|
40
|
+
"THE TIME TO START THE STEP RUN IS TOO LONG, THE MAIN THREAD MAY BE BLOCKED"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def noop_handler():
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class WorkerActionListenerProcess:
|
|
50
|
+
name: str
|
|
51
|
+
actions: List[str]
|
|
52
|
+
max_runs: int
|
|
53
|
+
config: ClientConfig
|
|
54
|
+
action_queue: Queue
|
|
55
|
+
event_queue: Queue
|
|
56
|
+
handle_kill: bool = True
|
|
57
|
+
debug: bool = False
|
|
58
|
+
labels: dict = field(default_factory=dict)
|
|
59
|
+
|
|
60
|
+
listener: ActionListener = field(init=False, default=None)
|
|
61
|
+
|
|
62
|
+
killing: bool = field(init=False, default=False)
|
|
63
|
+
|
|
64
|
+
action_loop_task: asyncio.Task = field(init=False, default=None)
|
|
65
|
+
event_send_loop_task: asyncio.Task = field(init=False, default=None)
|
|
66
|
+
|
|
67
|
+
running_step_runs: Mapping[str, float] = field(init=False, default_factory=dict)
|
|
68
|
+
|
|
69
|
+
def __post_init__(self):
|
|
70
|
+
if self.debug:
|
|
71
|
+
logger.setLevel(logging.DEBUG)
|
|
72
|
+
|
|
73
|
+
loop = asyncio.get_event_loop()
|
|
74
|
+
loop.add_signal_handler(signal.SIGINT, noop_handler)
|
|
75
|
+
loop.add_signal_handler(signal.SIGTERM, noop_handler)
|
|
76
|
+
loop.add_signal_handler(
|
|
77
|
+
signal.SIGQUIT, lambda: asyncio.create_task(self.exit_gracefully())
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
async def start(self, retry_attempt=0):
|
|
81
|
+
if retry_attempt > 5:
|
|
82
|
+
logger.error("could not start action listener")
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
logger.debug(f"starting action listener: {self.name}")
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
self.dispatcher_client = new_dispatcher(self.config)
|
|
89
|
+
|
|
90
|
+
self.listener = await self.dispatcher_client.get_action_listener(
|
|
91
|
+
GetActionListenerRequest(
|
|
92
|
+
worker_name=self.name,
|
|
93
|
+
services=["default"],
|
|
94
|
+
actions=self.actions,
|
|
95
|
+
max_runs=self.max_runs,
|
|
96
|
+
_labels=self.labels,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
logger.debug(f"acquired action listener: {self.listener.worker_id}")
|
|
101
|
+
except grpc.RpcError as rpc_error:
|
|
102
|
+
logger.error(f"could not start action listener: {rpc_error}")
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
# Start both loops as background tasks
|
|
106
|
+
self.action_loop_task = asyncio.create_task(self.start_action_loop())
|
|
107
|
+
self.event_send_loop_task = asyncio.create_task(self.start_event_send_loop())
|
|
108
|
+
self.blocked_main_loop = asyncio.create_task(self.start_blocked_main_loop())
|
|
109
|
+
|
|
110
|
+
# TODO move event methods to separate class
|
|
111
|
+
async def _get_event(self):
|
|
112
|
+
loop = asyncio.get_running_loop()
|
|
113
|
+
return await loop.run_in_executor(None, self.event_queue.get)
|
|
114
|
+
|
|
115
|
+
async def start_event_send_loop(self):
|
|
116
|
+
while True:
|
|
117
|
+
event: ActionEvent = await self._get_event()
|
|
118
|
+
if event == STOP_LOOP:
|
|
119
|
+
logger.debug("stopping event send loop...")
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
logger.debug(f"tx: event: {event.action.action_id}/{event.type}")
|
|
123
|
+
asyncio.create_task(self.send_event(event))
|
|
124
|
+
|
|
125
|
+
async def start_blocked_main_loop(self):
|
|
126
|
+
threshold = 1
|
|
127
|
+
while not self.killing:
|
|
128
|
+
count = 0
|
|
129
|
+
for step_run_id, start_time in self.running_step_runs.items():
|
|
130
|
+
diff = self.now() - start_time
|
|
131
|
+
if diff > threshold:
|
|
132
|
+
count += 1
|
|
133
|
+
|
|
134
|
+
if count > 0:
|
|
135
|
+
logger.warning(f"{BLOCKED_THREAD_WARNING}: Waiting Steps {count}")
|
|
136
|
+
await asyncio.sleep(1)
|
|
137
|
+
|
|
138
|
+
async def send_event(self, event: ActionEvent, retry_attempt: int = 1):
|
|
139
|
+
try:
|
|
140
|
+
match event.action.action_type:
|
|
141
|
+
# FIXME: all events sent from an execution of a function are of type ActionType.START_STEP_RUN since
|
|
142
|
+
# the action is re-used. We should change this.
|
|
143
|
+
case ActionType.START_STEP_RUN:
|
|
144
|
+
# TODO right now we're sending two start_step_run events
|
|
145
|
+
# one on the action loop and one on the event loop
|
|
146
|
+
# ideally we change the first to an ack to set the time
|
|
147
|
+
if event.type == STEP_EVENT_TYPE_STARTED:
|
|
148
|
+
if event.action.step_run_id in self.running_step_runs:
|
|
149
|
+
diff = (
|
|
150
|
+
self.now()
|
|
151
|
+
- self.running_step_runs[event.action.step_run_id]
|
|
152
|
+
)
|
|
153
|
+
if diff > 0.1:
|
|
154
|
+
logger.warning(
|
|
155
|
+
f"{BLOCKED_THREAD_WARNING}: time to start: {diff}s"
|
|
156
|
+
)
|
|
157
|
+
else:
|
|
158
|
+
logger.debug(f"start time: {diff}")
|
|
159
|
+
del self.running_step_runs[event.action.step_run_id]
|
|
160
|
+
else:
|
|
161
|
+
self.running_step_runs[event.action.step_run_id] = (
|
|
162
|
+
self.now()
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
asyncio.create_task(
|
|
166
|
+
self.dispatcher_client.send_step_action_event(
|
|
167
|
+
event.action, event.type, event.payload
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
case ActionType.CANCEL_STEP_RUN:
|
|
171
|
+
logger.debug("unimplemented event send")
|
|
172
|
+
case ActionType.START_GET_GROUP_KEY:
|
|
173
|
+
asyncio.create_task(
|
|
174
|
+
self.dispatcher_client.send_group_key_action_event(
|
|
175
|
+
event.action, event.type, event.payload
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
case _:
|
|
179
|
+
logger.error("unknown action type for event send")
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(
|
|
182
|
+
f"could not send action event ({retry_attempt}/{ACTION_EVENT_RETRY_COUNT}): {e}"
|
|
183
|
+
)
|
|
184
|
+
if retry_attempt <= ACTION_EVENT_RETRY_COUNT:
|
|
185
|
+
await exp_backoff_sleep(retry_attempt, 1)
|
|
186
|
+
await self.send_event(event, retry_attempt + 1)
|
|
187
|
+
|
|
188
|
+
def now(self):
|
|
189
|
+
return time.time()
|
|
190
|
+
|
|
191
|
+
async def start_action_loop(self):
|
|
192
|
+
try:
|
|
193
|
+
async for action in self.listener:
|
|
194
|
+
if action is None:
|
|
195
|
+
break
|
|
196
|
+
|
|
197
|
+
# Process the action here
|
|
198
|
+
match action.action_type:
|
|
199
|
+
case ActionType.START_STEP_RUN:
|
|
200
|
+
self.event_queue.put(
|
|
201
|
+
ActionEvent(
|
|
202
|
+
action=action,
|
|
203
|
+
type=STEP_EVENT_TYPE_STARTED, # TODO ack type
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
logger.info(
|
|
207
|
+
f"rx: start step run: {action.step_run_id}/{action.action_id}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# TODO handle this case better...
|
|
211
|
+
if action.step_run_id in self.running_step_runs:
|
|
212
|
+
logger.warning(
|
|
213
|
+
f"step run already running: {action.step_run_id}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
case ActionType.CANCEL_STEP_RUN:
|
|
217
|
+
logger.info(f"rx: cancel step run: {action.step_run_id}")
|
|
218
|
+
case ActionType.START_GET_GROUP_KEY:
|
|
219
|
+
self.event_queue.put(
|
|
220
|
+
ActionEvent(
|
|
221
|
+
action=action,
|
|
222
|
+
type=GROUP_KEY_EVENT_TYPE_STARTED, # TODO ack type
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
logger.info(
|
|
226
|
+
f"rx: start group key: {action.get_group_key_run_id}"
|
|
227
|
+
)
|
|
228
|
+
case _:
|
|
229
|
+
logger.error(
|
|
230
|
+
f"rx: unknown action type ({action.action_type}): {action.action_type}"
|
|
231
|
+
)
|
|
232
|
+
try:
|
|
233
|
+
self.action_queue.put(action)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"error putting action: {e}")
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
logger.error(f"error in action loop: {e}")
|
|
239
|
+
finally:
|
|
240
|
+
logger.info("action loop closed")
|
|
241
|
+
if not self.killing:
|
|
242
|
+
await self.exit_gracefully(skip_unregister=True)
|
|
243
|
+
|
|
244
|
+
async def cleanup(self):
|
|
245
|
+
self.killing = True
|
|
246
|
+
|
|
247
|
+
if self.listener is not None:
|
|
248
|
+
self.listener.cleanup()
|
|
249
|
+
|
|
250
|
+
self.event_queue.put(STOP_LOOP)
|
|
251
|
+
|
|
252
|
+
async def exit_gracefully(self, skip_unregister=False):
|
|
253
|
+
if self.killing:
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
logger.debug("closing action listener...")
|
|
257
|
+
|
|
258
|
+
await self.cleanup()
|
|
259
|
+
|
|
260
|
+
while not self.event_queue.empty():
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
logger.info("action listener closed")
|
|
264
|
+
|
|
265
|
+
def exit_forcefully(self):
|
|
266
|
+
asyncio.run(self.cleanup())
|
|
267
|
+
logger.debug("forcefully closing listener...")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def worker_action_listener_process(*args, **kwargs):
|
|
271
|
+
async def run():
|
|
272
|
+
process = WorkerActionListenerProcess(*args, **kwargs)
|
|
273
|
+
await process.start()
|
|
274
|
+
# Keep the process running
|
|
275
|
+
while not process.killing:
|
|
276
|
+
await asyncio.sleep(0.1)
|
|
277
|
+
|
|
278
|
+
asyncio.run(run())
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from multiprocessing import Queue
|
|
5
|
+
from typing import Callable, TypeVar
|
|
6
|
+
|
|
7
|
+
from hatchet_sdk.v0 import Context
|
|
8
|
+
from hatchet_sdk.v0.client import Client, new_client_raw
|
|
9
|
+
from hatchet_sdk.v0.clients.dispatcher.action_listener import Action
|
|
10
|
+
from hatchet_sdk.v0.loader import ClientConfig
|
|
11
|
+
from hatchet_sdk.v0.logger import logger
|
|
12
|
+
from hatchet_sdk.v0.utils.types import WorkflowValidator
|
|
13
|
+
from hatchet_sdk.v0.worker.runner.runner import Runner
|
|
14
|
+
from hatchet_sdk.v0.worker.runner.utils.capture_logs import capture_logs
|
|
15
|
+
|
|
16
|
+
STOP_LOOP = "STOP_LOOP"
|
|
17
|
+
|
|
18
|
+
T = TypeVar("T")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class WorkerActionRunLoopManager:
|
|
23
|
+
name: str
|
|
24
|
+
action_registry: dict[str, Callable[[Context], T]]
|
|
25
|
+
validator_registry: dict[str, WorkflowValidator]
|
|
26
|
+
max_runs: int | None
|
|
27
|
+
config: ClientConfig
|
|
28
|
+
action_queue: Queue
|
|
29
|
+
event_queue: Queue
|
|
30
|
+
loop: asyncio.AbstractEventLoop
|
|
31
|
+
handle_kill: bool = True
|
|
32
|
+
debug: bool = False
|
|
33
|
+
labels: dict[str, str | int] = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
client: Client = field(init=False, default=None)
|
|
36
|
+
|
|
37
|
+
killing: bool = field(init=False, default=False)
|
|
38
|
+
runner: Runner = field(init=False, default=None)
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
if self.debug:
|
|
42
|
+
logger.setLevel(logging.DEBUG)
|
|
43
|
+
self.client = new_client_raw(self.config, self.debug)
|
|
44
|
+
self.start()
|
|
45
|
+
|
|
46
|
+
def start(self, retry_count=1):
|
|
47
|
+
k = self.loop.create_task(self.async_start(retry_count))
|
|
48
|
+
|
|
49
|
+
async def async_start(self, retry_count=1):
|
|
50
|
+
await capture_logs(
|
|
51
|
+
self.client.logInterceptor,
|
|
52
|
+
self.client.event,
|
|
53
|
+
self._async_start,
|
|
54
|
+
)(retry_count=retry_count)
|
|
55
|
+
|
|
56
|
+
async def _async_start(self, retry_count: int = 1) -> None:
|
|
57
|
+
logger.info("starting runner...")
|
|
58
|
+
self.loop = asyncio.get_running_loop()
|
|
59
|
+
# needed for graceful termination
|
|
60
|
+
k = self.loop.create_task(self._start_action_loop())
|
|
61
|
+
await k
|
|
62
|
+
|
|
63
|
+
def cleanup(self) -> None:
|
|
64
|
+
self.killing = True
|
|
65
|
+
|
|
66
|
+
self.action_queue.put(STOP_LOOP)
|
|
67
|
+
|
|
68
|
+
async def wait_for_tasks(self) -> None:
|
|
69
|
+
if self.runner:
|
|
70
|
+
await self.runner.wait_for_tasks()
|
|
71
|
+
|
|
72
|
+
async def _start_action_loop(self) -> None:
|
|
73
|
+
self.runner = Runner(
|
|
74
|
+
self.name,
|
|
75
|
+
self.event_queue,
|
|
76
|
+
self.max_runs,
|
|
77
|
+
self.handle_kill,
|
|
78
|
+
self.action_registry,
|
|
79
|
+
self.validator_registry,
|
|
80
|
+
self.config,
|
|
81
|
+
self.labels,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
logger.debug(f"'{self.name}' waiting for {list(self.action_registry.keys())}")
|
|
85
|
+
while not self.killing:
|
|
86
|
+
action: Action = await self._get_action()
|
|
87
|
+
if action == STOP_LOOP:
|
|
88
|
+
logger.debug("stopping action runner loop...")
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
self.runner.run(action)
|
|
92
|
+
logger.debug("action runner loop stopped")
|
|
93
|
+
|
|
94
|
+
async def _get_action(self):
|
|
95
|
+
return await self.loop.run_in_executor(None, self.action_queue.get)
|
|
96
|
+
|
|
97
|
+
async def exit_gracefully(self) -> None:
|
|
98
|
+
if self.killing:
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
logger.info("gracefully exiting runner...")
|
|
102
|
+
|
|
103
|
+
self.cleanup()
|
|
104
|
+
|
|
105
|
+
# Wait for 1 second to allow last calls to flush. These are calls which have been
|
|
106
|
+
# added to the event loop as callbacks to tasks, so we're not aware of them in the
|
|
107
|
+
# task list.
|
|
108
|
+
await asyncio.sleep(1)
|
|
109
|
+
|
|
110
|
+
def exit_forcefully(self) -> None:
|
|
111
|
+
logger.info("forcefully exiting runner...")
|
|
112
|
+
self.cleanup()
|