hatchet-sdk 1.18.1__py3-none-any.whl → 1.19.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.
Potentially problematic release.
This version of hatchet-sdk might be problematic. Click here for more details.
- {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/METADATA +2 -3
- {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/RECORD +4 -225
- hatchet_sdk/v0/__init__.py +0 -251
- hatchet_sdk/v0/client.py +0 -119
- hatchet_sdk/v0/clients/admin.py +0 -541
- hatchet_sdk/v0/clients/dispatcher/action_listener.py +0 -422
- hatchet_sdk/v0/clients/dispatcher/dispatcher.py +0 -204
- hatchet_sdk/v0/clients/event_ts.py +0 -28
- hatchet_sdk/v0/clients/events.py +0 -182
- hatchet_sdk/v0/clients/rest/__init__.py +0 -307
- hatchet_sdk/v0/clients/rest/api/__init__.py +0 -19
- hatchet_sdk/v0/clients/rest/api/api_token_api.py +0 -858
- hatchet_sdk/v0/clients/rest/api/default_api.py +0 -2259
- hatchet_sdk/v0/clients/rest/api/event_api.py +0 -2548
- hatchet_sdk/v0/clients/rest/api/github_api.py +0 -331
- hatchet_sdk/v0/clients/rest/api/healthcheck_api.py +0 -483
- hatchet_sdk/v0/clients/rest/api/log_api.py +0 -449
- hatchet_sdk/v0/clients/rest/api/metadata_api.py +0 -728
- hatchet_sdk/v0/clients/rest/api/rate_limits_api.py +0 -423
- hatchet_sdk/v0/clients/rest/api/slack_api.py +0 -577
- hatchet_sdk/v0/clients/rest/api/sns_api.py +0 -872
- hatchet_sdk/v0/clients/rest/api/step_run_api.py +0 -2202
- hatchet_sdk/v0/clients/rest/api/tenant_api.py +0 -4430
- hatchet_sdk/v0/clients/rest/api/user_api.py +0 -2888
- hatchet_sdk/v0/clients/rest/api/worker_api.py +0 -858
- hatchet_sdk/v0/clients/rest/api/workflow_api.py +0 -6312
- hatchet_sdk/v0/clients/rest/api/workflow_run_api.py +0 -1932
- hatchet_sdk/v0/clients/rest/api/workflow_runs_api.py +0 -610
- hatchet_sdk/v0/clients/rest/api_client.py +0 -759
- hatchet_sdk/v0/clients/rest/api_response.py +0 -22
- hatchet_sdk/v0/clients/rest/configuration.py +0 -611
- hatchet_sdk/v0/clients/rest/exceptions.py +0 -200
- hatchet_sdk/v0/clients/rest/models/__init__.py +0 -274
- hatchet_sdk/v0/clients/rest/models/accept_invite_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/api_error.py +0 -102
- hatchet_sdk/v0/clients/rest/models/api_errors.py +0 -100
- hatchet_sdk/v0/clients/rest/models/api_meta.py +0 -144
- hatchet_sdk/v0/clients/rest/models/api_meta_auth.py +0 -85
- hatchet_sdk/v0/clients/rest/models/api_meta_integration.py +0 -88
- hatchet_sdk/v0/clients/rest/models/api_meta_posthog.py +0 -90
- hatchet_sdk/v0/clients/rest/models/api_resource_meta.py +0 -98
- hatchet_sdk/v0/clients/rest/models/api_token.py +0 -105
- hatchet_sdk/v0/clients/rest/models/bulk_create_event_request.py +0 -100
- hatchet_sdk/v0/clients/rest/models/bulk_create_event_response.py +0 -110
- hatchet_sdk/v0/clients/rest/models/cancel_event_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/cancel_step_run_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/concurrency_limit_strategy.py +0 -39
- hatchet_sdk/v0/clients/rest/models/create_api_token_request.py +0 -92
- hatchet_sdk/v0/clients/rest/models/create_api_token_response.py +0 -83
- hatchet_sdk/v0/clients/rest/models/create_cron_workflow_trigger_request.py +0 -98
- hatchet_sdk/v0/clients/rest/models/create_event_request.py +0 -95
- hatchet_sdk/v0/clients/rest/models/create_pull_request_from_step_run.py +0 -83
- hatchet_sdk/v0/clients/rest/models/create_sns_integration_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/create_tenant_alert_email_group_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/create_tenant_invite_request.py +0 -86
- hatchet_sdk/v0/clients/rest/models/create_tenant_request.py +0 -84
- hatchet_sdk/v0/clients/rest/models/cron_workflows.py +0 -131
- hatchet_sdk/v0/clients/rest/models/cron_workflows_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/cron_workflows_method.py +0 -37
- hatchet_sdk/v0/clients/rest/models/cron_workflows_order_by_field.py +0 -37
- hatchet_sdk/v0/clients/rest/models/event.py +0 -143
- hatchet_sdk/v0/clients/rest/models/event_data.py +0 -83
- hatchet_sdk/v0/clients/rest/models/event_key_list.py +0 -98
- hatchet_sdk/v0/clients/rest/models/event_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/event_order_by_direction.py +0 -37
- hatchet_sdk/v0/clients/rest/models/event_order_by_field.py +0 -36
- hatchet_sdk/v0/clients/rest/models/event_update_cancel200_response.py +0 -85
- hatchet_sdk/v0/clients/rest/models/event_workflow_run_summary.py +0 -116
- hatchet_sdk/v0/clients/rest/models/events.py +0 -110
- hatchet_sdk/v0/clients/rest/models/get_step_run_diff_response.py +0 -100
- hatchet_sdk/v0/clients/rest/models/github_app_installation.py +0 -107
- hatchet_sdk/v0/clients/rest/models/github_branch.py +0 -86
- hatchet_sdk/v0/clients/rest/models/github_repo.py +0 -86
- hatchet_sdk/v0/clients/rest/models/info_get_version200_response.py +0 -83
- hatchet_sdk/v0/clients/rest/models/job.py +0 -132
- hatchet_sdk/v0/clients/rest/models/job_run.py +0 -176
- hatchet_sdk/v0/clients/rest/models/job_run_status.py +0 -41
- hatchet_sdk/v0/clients/rest/models/link_github_repository_request.py +0 -106
- hatchet_sdk/v0/clients/rest/models/list_api_tokens_response.py +0 -110
- hatchet_sdk/v0/clients/rest/models/list_github_app_installations_response.py +0 -112
- hatchet_sdk/v0/clients/rest/models/list_pull_requests_response.py +0 -100
- hatchet_sdk/v0/clients/rest/models/list_slack_webhooks.py +0 -110
- hatchet_sdk/v0/clients/rest/models/list_sns_integrations.py +0 -110
- hatchet_sdk/v0/clients/rest/models/log_line.py +0 -94
- hatchet_sdk/v0/clients/rest/models/log_line_level.py +0 -39
- hatchet_sdk/v0/clients/rest/models/log_line_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/log_line_order_by_direction.py +0 -37
- hatchet_sdk/v0/clients/rest/models/log_line_order_by_field.py +0 -36
- hatchet_sdk/v0/clients/rest/models/pagination_response.py +0 -95
- hatchet_sdk/v0/clients/rest/models/pull_request.py +0 -112
- hatchet_sdk/v0/clients/rest/models/pull_request_state.py +0 -37
- hatchet_sdk/v0/clients/rest/models/queue_metrics.py +0 -97
- hatchet_sdk/v0/clients/rest/models/rate_limit.py +0 -117
- hatchet_sdk/v0/clients/rest/models/rate_limit_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_direction.py +0 -37
- hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_field.py +0 -38
- hatchet_sdk/v0/clients/rest/models/recent_step_runs.py +0 -118
- hatchet_sdk/v0/clients/rest/models/reject_invite_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/replay_event_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_response.py +0 -100
- hatchet_sdk/v0/clients/rest/models/rerun_step_run_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/schedule_workflow_run_request.py +0 -92
- hatchet_sdk/v0/clients/rest/models/scheduled_run_status.py +0 -42
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows.py +0 -149
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_method.py +0 -37
- hatchet_sdk/v0/clients/rest/models/scheduled_workflows_order_by_field.py +0 -37
- hatchet_sdk/v0/clients/rest/models/semaphore_slots.py +0 -113
- hatchet_sdk/v0/clients/rest/models/slack_webhook.py +0 -127
- hatchet_sdk/v0/clients/rest/models/sns_integration.py +0 -114
- hatchet_sdk/v0/clients/rest/models/step.py +0 -123
- hatchet_sdk/v0/clients/rest/models/step_run.py +0 -202
- hatchet_sdk/v0/clients/rest/models/step_run_archive.py +0 -142
- hatchet_sdk/v0/clients/rest/models/step_run_archive_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/step_run_diff.py +0 -91
- hatchet_sdk/v0/clients/rest/models/step_run_event.py +0 -122
- hatchet_sdk/v0/clients/rest/models/step_run_event_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/step_run_event_reason.py +0 -52
- hatchet_sdk/v0/clients/rest/models/step_run_event_severity.py +0 -38
- hatchet_sdk/v0/clients/rest/models/step_run_status.py +0 -44
- hatchet_sdk/v0/clients/rest/models/tenant.py +0 -118
- hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group.py +0 -98
- hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group_list.py +0 -112
- hatchet_sdk/v0/clients/rest/models/tenant_alerting_settings.py +0 -143
- hatchet_sdk/v0/clients/rest/models/tenant_invite.py +0 -120
- hatchet_sdk/v0/clients/rest/models/tenant_invite_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/tenant_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/tenant_member.py +0 -123
- hatchet_sdk/v0/clients/rest/models/tenant_member_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/tenant_member_role.py +0 -38
- hatchet_sdk/v0/clients/rest/models/tenant_queue_metrics.py +0 -116
- hatchet_sdk/v0/clients/rest/models/tenant_resource.py +0 -40
- hatchet_sdk/v0/clients/rest/models/tenant_resource_limit.py +0 -135
- hatchet_sdk/v0/clients/rest/models/tenant_resource_policy.py +0 -102
- hatchet_sdk/v0/clients/rest/models/tenant_step_run_queue_metrics.py +0 -83
- hatchet_sdk/v0/clients/rest/models/trigger_workflow_run_request.py +0 -91
- hatchet_sdk/v0/clients/rest/models/update_tenant_alert_email_group_request.py +0 -83
- hatchet_sdk/v0/clients/rest/models/update_tenant_invite_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/update_tenant_request.py +0 -137
- hatchet_sdk/v0/clients/rest/models/update_worker_request.py +0 -87
- hatchet_sdk/v0/clients/rest/models/user.py +0 -126
- hatchet_sdk/v0/clients/rest/models/user_change_password_request.py +0 -88
- hatchet_sdk/v0/clients/rest/models/user_login_request.py +0 -86
- hatchet_sdk/v0/clients/rest/models/user_register_request.py +0 -91
- hatchet_sdk/v0/clients/rest/models/user_tenant_memberships_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/user_tenant_public.py +0 -86
- hatchet_sdk/v0/clients/rest/models/webhook_worker.py +0 -100
- hatchet_sdk/v0/clients/rest/models/webhook_worker_create_request.py +0 -94
- hatchet_sdk/v0/clients/rest/models/webhook_worker_create_response.py +0 -98
- hatchet_sdk/v0/clients/rest/models/webhook_worker_created.py +0 -102
- hatchet_sdk/v0/clients/rest/models/webhook_worker_list_response.py +0 -110
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request.py +0 -102
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request_list_response.py +0 -104
- hatchet_sdk/v0/clients/rest/models/webhook_worker_request_method.py +0 -38
- hatchet_sdk/v0/clients/rest/models/worker.py +0 -239
- hatchet_sdk/v0/clients/rest/models/worker_label.py +0 -102
- hatchet_sdk/v0/clients/rest/models/worker_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/worker_runtime_info.py +0 -103
- hatchet_sdk/v0/clients/rest/models/worker_runtime_sdks.py +0 -38
- hatchet_sdk/v0/clients/rest/models/worker_type.py +0 -38
- hatchet_sdk/v0/clients/rest/models/workflow.py +0 -165
- hatchet_sdk/v0/clients/rest/models/workflow_concurrency.py +0 -107
- hatchet_sdk/v0/clients/rest/models/workflow_deployment_config.py +0 -136
- hatchet_sdk/v0/clients/rest/models/workflow_kind.py +0 -38
- hatchet_sdk/v0/clients/rest/models/workflow_list.py +0 -120
- hatchet_sdk/v0/clients/rest/models/workflow_metrics.py +0 -97
- hatchet_sdk/v0/clients/rest/models/workflow_run.py +0 -188
- hatchet_sdk/v0/clients/rest/models/workflow_run_cancel200_response.py +0 -85
- hatchet_sdk/v0/clients/rest/models/workflow_run_list.py +0 -110
- hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_direction.py +0 -37
- hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_field.py +0 -39
- hatchet_sdk/v0/clients/rest/models/workflow_run_shape.py +0 -186
- hatchet_sdk/v0/clients/rest/models/workflow_run_status.py +0 -42
- hatchet_sdk/v0/clients/rest/models/workflow_run_triggered_by.py +0 -112
- hatchet_sdk/v0/clients/rest/models/workflow_runs_cancel_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics.py +0 -94
- hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics_counts.py +0 -104
- hatchet_sdk/v0/clients/rest/models/workflow_tag.py +0 -84
- hatchet_sdk/v0/clients/rest/models/workflow_trigger_cron_ref.py +0 -86
- hatchet_sdk/v0/clients/rest/models/workflow_trigger_event_ref.py +0 -86
- hatchet_sdk/v0/clients/rest/models/workflow_triggers.py +0 -141
- hatchet_sdk/v0/clients/rest/models/workflow_update_request.py +0 -85
- hatchet_sdk/v0/clients/rest/models/workflow_version.py +0 -170
- hatchet_sdk/v0/clients/rest/models/workflow_version_concurrency.py +0 -114
- hatchet_sdk/v0/clients/rest/models/workflow_version_definition.py +0 -85
- hatchet_sdk/v0/clients/rest/models/workflow_version_meta.py +0 -123
- hatchet_sdk/v0/clients/rest/models/workflow_workers_count.py +0 -95
- hatchet_sdk/v0/clients/rest/rest.py +0 -187
- hatchet_sdk/v0/clients/rest/tenacity_utils.py +0 -39
- hatchet_sdk/v0/clients/rest_client.py +0 -622
- hatchet_sdk/v0/clients/run_event_listener.py +0 -260
- hatchet_sdk/v0/clients/workflow_listener.py +0 -277
- hatchet_sdk/v0/connection.py +0 -63
- hatchet_sdk/v0/context/__init__.py +0 -1
- hatchet_sdk/v0/context/context.py +0 -446
- hatchet_sdk/v0/context/worker_context.py +0 -28
- hatchet_sdk/v0/features/cron.py +0 -286
- hatchet_sdk/v0/features/scheduled.py +0 -248
- hatchet_sdk/v0/hatchet.py +0 -310
- hatchet_sdk/v0/labels.py +0 -10
- hatchet_sdk/v0/loader.py +0 -244
- hatchet_sdk/v0/metadata.py +0 -2
- hatchet_sdk/v0/opentelemetry/instrumentor.py +0 -393
- hatchet_sdk/v0/rate_limit.py +0 -126
- hatchet_sdk/v0/semver.py +0 -30
- hatchet_sdk/v0/token.py +0 -27
- hatchet_sdk/v0/utils/aio_utils.py +0 -137
- hatchet_sdk/v0/utils/backoff.py +0 -9
- hatchet_sdk/v0/utils/types.py +0 -8
- hatchet_sdk/v0/utils/typing.py +0 -12
- hatchet_sdk/v0/v2/callable.py +0 -202
- hatchet_sdk/v0/v2/concurrency.py +0 -47
- hatchet_sdk/v0/v2/hatchet.py +0 -224
- hatchet_sdk/v0/worker/__init__.py +0 -1
- hatchet_sdk/v0/worker/action_listener_process.py +0 -294
- hatchet_sdk/v0/worker/runner/run_loop_manager.py +0 -112
- hatchet_sdk/v0/worker/runner/runner.py +0 -460
- hatchet_sdk/v0/worker/runner/utils/capture_logs.py +0 -81
- hatchet_sdk/v0/worker/runner/utils/error_with_traceback.py +0 -6
- hatchet_sdk/v0/worker/worker.py +0 -391
- hatchet_sdk/v0/workflow.py +0 -261
- hatchet_sdk/v0/workflow_run.py +0 -59
- {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/WHEEL +0 -0
- {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,460 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import contextvars
|
|
3
|
-
import ctypes
|
|
4
|
-
import functools
|
|
5
|
-
import json
|
|
6
|
-
import time
|
|
7
|
-
import traceback
|
|
8
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
9
|
-
from enum import Enum
|
|
10
|
-
from multiprocessing import Queue
|
|
11
|
-
from threading import Thread, current_thread
|
|
12
|
-
from typing import Any, Callable, Dict, cast
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel
|
|
15
|
-
|
|
16
|
-
from hatchet_sdk.contracts.dispatcher_pb2 import (
|
|
17
|
-
GROUP_KEY_EVENT_TYPE_COMPLETED,
|
|
18
|
-
GROUP_KEY_EVENT_TYPE_FAILED,
|
|
19
|
-
GROUP_KEY_EVENT_TYPE_STARTED,
|
|
20
|
-
STEP_EVENT_TYPE_COMPLETED,
|
|
21
|
-
STEP_EVENT_TYPE_FAILED,
|
|
22
|
-
STEP_EVENT_TYPE_STARTED,
|
|
23
|
-
ActionType,
|
|
24
|
-
)
|
|
25
|
-
from hatchet_sdk.logger import logger
|
|
26
|
-
from hatchet_sdk.v0.client import new_client_raw
|
|
27
|
-
from hatchet_sdk.v0.clients.admin import new_admin
|
|
28
|
-
from hatchet_sdk.v0.clients.dispatcher.action_listener import Action
|
|
29
|
-
from hatchet_sdk.v0.clients.dispatcher.dispatcher import new_dispatcher
|
|
30
|
-
from hatchet_sdk.v0.clients.run_event_listener import new_listener
|
|
31
|
-
from hatchet_sdk.v0.clients.workflow_listener import PooledWorkflowRunListener
|
|
32
|
-
from hatchet_sdk.v0.context import Context # type: ignore[attr-defined]
|
|
33
|
-
from hatchet_sdk.v0.context.worker_context import WorkerContext
|
|
34
|
-
from hatchet_sdk.v0.loader import ClientConfig
|
|
35
|
-
from hatchet_sdk.v0.utils.types import WorkflowValidator
|
|
36
|
-
from hatchet_sdk.v0.v2.callable import DurableContext
|
|
37
|
-
from hatchet_sdk.v0.worker.action_listener_process import ActionEvent
|
|
38
|
-
from hatchet_sdk.v0.worker.runner.utils.capture_logs import copy_context_vars, sr, wr
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class WorkerStatus(Enum):
|
|
42
|
-
INITIALIZED = 1
|
|
43
|
-
STARTING = 2
|
|
44
|
-
HEALTHY = 3
|
|
45
|
-
UNHEALTHY = 4
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class Runner:
|
|
49
|
-
def __init__(
|
|
50
|
-
self,
|
|
51
|
-
name: str,
|
|
52
|
-
event_queue: "Queue[Any]",
|
|
53
|
-
max_runs: int | None = None,
|
|
54
|
-
handle_kill: bool = True,
|
|
55
|
-
action_registry: dict[str, Callable[..., Any]] = {},
|
|
56
|
-
validator_registry: dict[str, WorkflowValidator] = {},
|
|
57
|
-
config: ClientConfig = ClientConfig(),
|
|
58
|
-
labels: dict[str, str | int] = {},
|
|
59
|
-
):
|
|
60
|
-
# We store the config so we can dynamically create clients for the dispatcher client.
|
|
61
|
-
self.config = config
|
|
62
|
-
self.client = new_client_raw(config)
|
|
63
|
-
self.name = self.client.config.namespace + name
|
|
64
|
-
self.max_runs = max_runs
|
|
65
|
-
self.tasks: dict[str, asyncio.Task[Any]] = {} # Store run ids and futures
|
|
66
|
-
self.contexts: dict[str, Context] = {} # Store run ids and contexts
|
|
67
|
-
self.action_registry: dict[str, Callable[..., Any]] = action_registry
|
|
68
|
-
self.validator_registry = validator_registry
|
|
69
|
-
|
|
70
|
-
self.event_queue = event_queue
|
|
71
|
-
|
|
72
|
-
# The thread pool is used for synchronous functions which need to run concurrently
|
|
73
|
-
self.thread_pool = ThreadPoolExecutor(max_workers=max_runs)
|
|
74
|
-
self.threads: Dict[str, Thread] = {} # Store run ids and threads
|
|
75
|
-
|
|
76
|
-
self.killing = False
|
|
77
|
-
self.handle_kill = handle_kill
|
|
78
|
-
|
|
79
|
-
# We need to initialize a new admin and dispatcher client *after* we've started the event loop,
|
|
80
|
-
# otherwise the grpc.aio methods will use a different event loop and we'll get a bunch of errors.
|
|
81
|
-
self.dispatcher_client = new_dispatcher(self.config)
|
|
82
|
-
self.admin_client = new_admin(self.config)
|
|
83
|
-
self.workflow_run_event_listener = new_listener(self.config)
|
|
84
|
-
self.client.workflow_listener = PooledWorkflowRunListener(self.config)
|
|
85
|
-
|
|
86
|
-
self.worker_context = WorkerContext(
|
|
87
|
-
labels=labels, client=new_client_raw(config).dispatcher
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
def create_workflow_run_url(self, action: Action) -> str:
|
|
91
|
-
return f"{self.config.server_url}/workflow-runs/{action.workflow_run_id}?tenant={action.tenant_id}"
|
|
92
|
-
|
|
93
|
-
def run(self, action: Action) -> None:
|
|
94
|
-
if self.worker_context.id() is None:
|
|
95
|
-
self.worker_context._worker_id = action.worker_id
|
|
96
|
-
|
|
97
|
-
match action.action_type:
|
|
98
|
-
case ActionType.START_STEP_RUN:
|
|
99
|
-
log = f"run: start step: {action.action_id}/{action.step_run_id}"
|
|
100
|
-
logger.info(log)
|
|
101
|
-
asyncio.create_task(self.handle_start_step_run(action))
|
|
102
|
-
case ActionType.CANCEL_STEP_RUN:
|
|
103
|
-
log = f"cancel: step run: {action.action_id}/{action.step_run_id}"
|
|
104
|
-
logger.info(log)
|
|
105
|
-
asyncio.create_task(self.handle_cancel_action(action.step_run_id))
|
|
106
|
-
case ActionType.START_GET_GROUP_KEY:
|
|
107
|
-
log = f"run: get group key: {action.action_id}/{action.get_group_key_run_id}"
|
|
108
|
-
logger.info(log)
|
|
109
|
-
asyncio.create_task(self.handle_start_group_key_run(action))
|
|
110
|
-
case _:
|
|
111
|
-
log = f"unknown action type: {action.action_type}"
|
|
112
|
-
logger.error(log)
|
|
113
|
-
|
|
114
|
-
def step_run_callback(self, action: Action) -> Callable[[asyncio.Task[Any]], None]:
|
|
115
|
-
def inner_callback(task: asyncio.Task[Any]) -> None:
|
|
116
|
-
self.cleanup_run_id(action.step_run_id)
|
|
117
|
-
|
|
118
|
-
errored = False
|
|
119
|
-
cancelled = task.cancelled()
|
|
120
|
-
|
|
121
|
-
# Get the output from the future
|
|
122
|
-
try:
|
|
123
|
-
if not cancelled:
|
|
124
|
-
output = task.result()
|
|
125
|
-
except Exception as e:
|
|
126
|
-
errored = True
|
|
127
|
-
|
|
128
|
-
# This except is coming from the application itself, so we want to send that to the Hatchet instance
|
|
129
|
-
self.event_queue.put(
|
|
130
|
-
ActionEvent(
|
|
131
|
-
action=action,
|
|
132
|
-
type=STEP_EVENT_TYPE_FAILED,
|
|
133
|
-
payload=str(errorWithTraceback(f"{e}", e)),
|
|
134
|
-
)
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
logger.error(
|
|
138
|
-
f"failed step run: {action.action_id}/{action.step_run_id}"
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
if not errored and not cancelled:
|
|
142
|
-
self.event_queue.put(
|
|
143
|
-
ActionEvent(
|
|
144
|
-
action=action,
|
|
145
|
-
type=STEP_EVENT_TYPE_COMPLETED,
|
|
146
|
-
payload=self.serialize_output(output),
|
|
147
|
-
)
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
logger.info(
|
|
151
|
-
f"finished step run: {action.action_id}/{action.step_run_id}"
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return inner_callback
|
|
155
|
-
|
|
156
|
-
def group_key_run_callback(
|
|
157
|
-
self, action: Action
|
|
158
|
-
) -> Callable[[asyncio.Task[Any]], None]:
|
|
159
|
-
def inner_callback(task: asyncio.Task[Any]) -> None:
|
|
160
|
-
self.cleanup_run_id(action.get_group_key_run_id)
|
|
161
|
-
|
|
162
|
-
errored = False
|
|
163
|
-
cancelled = task.cancelled()
|
|
164
|
-
|
|
165
|
-
# Get the output from the future
|
|
166
|
-
try:
|
|
167
|
-
if not cancelled:
|
|
168
|
-
output = task.result()
|
|
169
|
-
except Exception as e:
|
|
170
|
-
errored = True
|
|
171
|
-
self.event_queue.put(
|
|
172
|
-
ActionEvent(
|
|
173
|
-
action=action,
|
|
174
|
-
type=GROUP_KEY_EVENT_TYPE_FAILED,
|
|
175
|
-
payload=str(errorWithTraceback(f"{e}", e)),
|
|
176
|
-
)
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
logger.error(
|
|
180
|
-
f"failed step run: {action.action_id}/{action.step_run_id}"
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if not errored and not cancelled:
|
|
184
|
-
self.event_queue.put(
|
|
185
|
-
ActionEvent(
|
|
186
|
-
action=action,
|
|
187
|
-
type=GROUP_KEY_EVENT_TYPE_COMPLETED,
|
|
188
|
-
payload=self.serialize_output(output),
|
|
189
|
-
)
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
logger.info(
|
|
193
|
-
f"finished step run: {action.action_id}/{action.step_run_id}"
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
return inner_callback
|
|
197
|
-
|
|
198
|
-
## TODO: Stricter type hinting here
|
|
199
|
-
def thread_action_func(
|
|
200
|
-
self, context: Context, action_func: Callable[..., Any], action: Action
|
|
201
|
-
) -> Any:
|
|
202
|
-
if action.step_run_id is not None and action.step_run_id != "":
|
|
203
|
-
self.threads[action.step_run_id] = current_thread()
|
|
204
|
-
elif (
|
|
205
|
-
action.get_group_key_run_id is not None
|
|
206
|
-
and action.get_group_key_run_id != ""
|
|
207
|
-
):
|
|
208
|
-
self.threads[action.get_group_key_run_id] = current_thread()
|
|
209
|
-
|
|
210
|
-
return action_func(context)
|
|
211
|
-
|
|
212
|
-
## TODO: Stricter type hinting here
|
|
213
|
-
# We wrap all actions in an async func
|
|
214
|
-
async def async_wrapped_action_func(
|
|
215
|
-
self,
|
|
216
|
-
context: Context,
|
|
217
|
-
action_func: Callable[..., Any],
|
|
218
|
-
action: Action,
|
|
219
|
-
run_id: str,
|
|
220
|
-
) -> Any:
|
|
221
|
-
wr.set(context.workflow_run_id())
|
|
222
|
-
sr.set(context.step_run_id)
|
|
223
|
-
|
|
224
|
-
try:
|
|
225
|
-
if (
|
|
226
|
-
hasattr(action_func, "is_coroutine") and action_func.is_coroutine
|
|
227
|
-
) or asyncio.iscoroutinefunction(action_func):
|
|
228
|
-
return await action_func(context)
|
|
229
|
-
else:
|
|
230
|
-
pfunc = functools.partial(
|
|
231
|
-
# we must copy the context vars to the new thread, as only asyncio natively supports
|
|
232
|
-
# contextvars
|
|
233
|
-
copy_context_vars,
|
|
234
|
-
contextvars.copy_context().items(),
|
|
235
|
-
self.thread_action_func,
|
|
236
|
-
context,
|
|
237
|
-
action_func,
|
|
238
|
-
action,
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
loop = asyncio.get_event_loop()
|
|
242
|
-
return await loop.run_in_executor(self.thread_pool, pfunc)
|
|
243
|
-
except Exception as e:
|
|
244
|
-
logger.error(
|
|
245
|
-
errorWithTraceback(
|
|
246
|
-
f"exception raised in action ({action.action_id}, retry={action.retry_count}):\n{e}",
|
|
247
|
-
e,
|
|
248
|
-
)
|
|
249
|
-
)
|
|
250
|
-
raise e
|
|
251
|
-
finally:
|
|
252
|
-
self.cleanup_run_id(run_id)
|
|
253
|
-
|
|
254
|
-
def cleanup_run_id(self, run_id: str | None) -> None:
|
|
255
|
-
if run_id in self.tasks:
|
|
256
|
-
del self.tasks[run_id]
|
|
257
|
-
|
|
258
|
-
if run_id in self.threads:
|
|
259
|
-
del self.threads[run_id]
|
|
260
|
-
|
|
261
|
-
if run_id in self.contexts:
|
|
262
|
-
del self.contexts[run_id]
|
|
263
|
-
|
|
264
|
-
def create_context(
|
|
265
|
-
self, action: Action, action_func: Callable[..., Any] | None
|
|
266
|
-
) -> Context | DurableContext:
|
|
267
|
-
if hasattr(action_func, "durable") and getattr(action_func, "durable"):
|
|
268
|
-
return DurableContext(
|
|
269
|
-
action,
|
|
270
|
-
self.dispatcher_client,
|
|
271
|
-
self.admin_client,
|
|
272
|
-
self.client.event,
|
|
273
|
-
self.client.rest,
|
|
274
|
-
self.client.workflow_listener,
|
|
275
|
-
self.workflow_run_event_listener,
|
|
276
|
-
self.worker_context,
|
|
277
|
-
self.client.config.namespace,
|
|
278
|
-
validator_registry=self.validator_registry,
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
return Context(
|
|
282
|
-
action,
|
|
283
|
-
self.dispatcher_client,
|
|
284
|
-
self.admin_client,
|
|
285
|
-
self.client.event,
|
|
286
|
-
self.client.rest,
|
|
287
|
-
self.client.workflow_listener,
|
|
288
|
-
self.workflow_run_event_listener,
|
|
289
|
-
self.worker_context,
|
|
290
|
-
self.client.config.namespace,
|
|
291
|
-
validator_registry=self.validator_registry,
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
## IMPORTANT: Keep this method's signature in sync with the wrapper in the OTel instrumentor
|
|
295
|
-
async def handle_start_step_run(self, action: Action) -> None | Exception:
|
|
296
|
-
action_name = action.action_id
|
|
297
|
-
|
|
298
|
-
# Find the corresponding action function from the registry
|
|
299
|
-
action_func = self.action_registry.get(action_name)
|
|
300
|
-
|
|
301
|
-
context = self.create_context(action, action_func)
|
|
302
|
-
|
|
303
|
-
self.contexts[action.step_run_id] = context
|
|
304
|
-
|
|
305
|
-
if action_func:
|
|
306
|
-
self.event_queue.put(
|
|
307
|
-
ActionEvent(
|
|
308
|
-
action=action,
|
|
309
|
-
type=STEP_EVENT_TYPE_STARTED,
|
|
310
|
-
)
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
loop = asyncio.get_event_loop()
|
|
314
|
-
task = loop.create_task(
|
|
315
|
-
self.async_wrapped_action_func(
|
|
316
|
-
context, action_func, action, action.step_run_id
|
|
317
|
-
)
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
task.add_done_callback(self.step_run_callback(action))
|
|
321
|
-
self.tasks[action.step_run_id] = task
|
|
322
|
-
|
|
323
|
-
try:
|
|
324
|
-
await task
|
|
325
|
-
except Exception as e:
|
|
326
|
-
return e
|
|
327
|
-
|
|
328
|
-
return None
|
|
329
|
-
|
|
330
|
-
## IMPORTANT: Keep this method's signature in sync with the wrapper in the OTel instrumentor
|
|
331
|
-
async def handle_start_group_key_run(self, action: Action) -> Exception | None:
|
|
332
|
-
action_name = action.action_id
|
|
333
|
-
context = Context(
|
|
334
|
-
action,
|
|
335
|
-
self.dispatcher_client,
|
|
336
|
-
self.admin_client,
|
|
337
|
-
self.client.event,
|
|
338
|
-
self.client.rest,
|
|
339
|
-
self.client.workflow_listener,
|
|
340
|
-
self.workflow_run_event_listener,
|
|
341
|
-
self.worker_context,
|
|
342
|
-
self.client.config.namespace,
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
self.contexts[action.get_group_key_run_id] = context
|
|
346
|
-
|
|
347
|
-
# Find the corresponding action function from the registry
|
|
348
|
-
action_func = self.action_registry.get(action_name)
|
|
349
|
-
|
|
350
|
-
if action_func:
|
|
351
|
-
# send an event that the group key run has started
|
|
352
|
-
self.event_queue.put(
|
|
353
|
-
ActionEvent(
|
|
354
|
-
action=action,
|
|
355
|
-
type=GROUP_KEY_EVENT_TYPE_STARTED,
|
|
356
|
-
)
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
loop = asyncio.get_event_loop()
|
|
360
|
-
task = loop.create_task(
|
|
361
|
-
self.async_wrapped_action_func(
|
|
362
|
-
context, action_func, action, action.get_group_key_run_id
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
task.add_done_callback(self.group_key_run_callback(action))
|
|
367
|
-
self.tasks[action.get_group_key_run_id] = task
|
|
368
|
-
|
|
369
|
-
try:
|
|
370
|
-
await task
|
|
371
|
-
except Exception as e:
|
|
372
|
-
return e
|
|
373
|
-
|
|
374
|
-
return None
|
|
375
|
-
|
|
376
|
-
def force_kill_thread(self, thread: Thread) -> None:
|
|
377
|
-
"""Terminate a python threading.Thread."""
|
|
378
|
-
try:
|
|
379
|
-
if not thread.is_alive():
|
|
380
|
-
return
|
|
381
|
-
|
|
382
|
-
ident = cast(int, thread.ident)
|
|
383
|
-
|
|
384
|
-
logger.info(f"Forcefully terminating thread {ident}")
|
|
385
|
-
|
|
386
|
-
exc = ctypes.py_object(SystemExit)
|
|
387
|
-
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(ident), exc)
|
|
388
|
-
if res == 0:
|
|
389
|
-
raise ValueError("Invalid thread ID")
|
|
390
|
-
elif res != 1:
|
|
391
|
-
logger.error("PyThreadState_SetAsyncExc failed")
|
|
392
|
-
|
|
393
|
-
# Call with exception set to 0 is needed to cleanup properly.
|
|
394
|
-
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, 0)
|
|
395
|
-
raise SystemError("PyThreadState_SetAsyncExc failed")
|
|
396
|
-
|
|
397
|
-
logger.info(f"Successfully terminated thread {ident}")
|
|
398
|
-
|
|
399
|
-
# Immediately add a new thread to the thread pool, because we've actually killed a worker
|
|
400
|
-
# in the ThreadPoolExecutor
|
|
401
|
-
self.thread_pool.submit(lambda: None)
|
|
402
|
-
except Exception as e:
|
|
403
|
-
logger.exception(f"Failed to terminate thread: {e}")
|
|
404
|
-
|
|
405
|
-
## IMPORTANT: Keep this method's signature in sync with the wrapper in the OTel instrumentor
|
|
406
|
-
async def handle_cancel_action(self, run_id: str) -> None:
|
|
407
|
-
try:
|
|
408
|
-
# call cancel to signal the context to stop
|
|
409
|
-
if run_id in self.contexts:
|
|
410
|
-
context = self.contexts.get(run_id)
|
|
411
|
-
|
|
412
|
-
if context:
|
|
413
|
-
context.cancel()
|
|
414
|
-
|
|
415
|
-
await asyncio.sleep(1)
|
|
416
|
-
|
|
417
|
-
if run_id in self.tasks:
|
|
418
|
-
future = self.tasks.get(run_id)
|
|
419
|
-
|
|
420
|
-
if future:
|
|
421
|
-
future.cancel()
|
|
422
|
-
|
|
423
|
-
# check if thread is still running, if so, print a warning
|
|
424
|
-
if run_id in self.threads:
|
|
425
|
-
thread = self.threads.get(run_id)
|
|
426
|
-
if thread and self.client.config.enable_force_kill_sync_threads:
|
|
427
|
-
self.force_kill_thread(thread)
|
|
428
|
-
await asyncio.sleep(1)
|
|
429
|
-
|
|
430
|
-
logger.warning(
|
|
431
|
-
f"Thread {self.threads[run_id].ident} with run id {run_id} is still running after cancellation. This could cause the thread pool to get blocked and prevent new tasks from running."
|
|
432
|
-
)
|
|
433
|
-
finally:
|
|
434
|
-
self.cleanup_run_id(run_id)
|
|
435
|
-
|
|
436
|
-
def serialize_output(self, output: Any) -> str:
|
|
437
|
-
|
|
438
|
-
if isinstance(output, BaseModel):
|
|
439
|
-
return output.model_dump_json()
|
|
440
|
-
|
|
441
|
-
if output is not None:
|
|
442
|
-
try:
|
|
443
|
-
return json.dumps(output)
|
|
444
|
-
except Exception as e:
|
|
445
|
-
logger.error(f"Could not serialize output: {e}")
|
|
446
|
-
return str(output)
|
|
447
|
-
|
|
448
|
-
return ""
|
|
449
|
-
|
|
450
|
-
async def wait_for_tasks(self) -> None:
|
|
451
|
-
running = len(self.tasks.keys())
|
|
452
|
-
while running > 0:
|
|
453
|
-
logger.info(f"waiting for {running} tasks to finish...")
|
|
454
|
-
await asyncio.sleep(1)
|
|
455
|
-
running = len(self.tasks.keys())
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
def errorWithTraceback(message: str, e: Exception) -> str:
|
|
459
|
-
trace = "".join(traceback.format_exception(type(e), e, e.__traceback__))
|
|
460
|
-
return f"{message}\n{trace}"
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import contextvars
|
|
2
|
-
import functools
|
|
3
|
-
import logging
|
|
4
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
-
from io import StringIO
|
|
6
|
-
from typing import Any, Coroutine
|
|
7
|
-
|
|
8
|
-
from hatchet_sdk import logger
|
|
9
|
-
from hatchet_sdk.v0.clients.events import EventClient
|
|
10
|
-
|
|
11
|
-
wr: contextvars.ContextVar[str | None] = contextvars.ContextVar(
|
|
12
|
-
"workflow_run_id", default=None
|
|
13
|
-
)
|
|
14
|
-
sr: contextvars.ContextVar[str | None] = contextvars.ContextVar(
|
|
15
|
-
"step_run_id", default=None
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def copy_context_vars(ctx_vars, func, *args, **kwargs):
|
|
20
|
-
for var, value in ctx_vars:
|
|
21
|
-
var.set(value)
|
|
22
|
-
return func(*args, **kwargs)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class InjectingFilter(logging.Filter):
|
|
26
|
-
# For some reason, only the InjectingFilter has access to the contextvars method sr.get(),
|
|
27
|
-
# otherwise we would use emit within the CustomLogHandler
|
|
28
|
-
def filter(self, record):
|
|
29
|
-
record.workflow_run_id = wr.get()
|
|
30
|
-
record.step_run_id = sr.get()
|
|
31
|
-
return True
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class CustomLogHandler(logging.StreamHandler):
|
|
35
|
-
def __init__(self, event_client: EventClient, stream=None):
|
|
36
|
-
super().__init__(stream)
|
|
37
|
-
self.logger_thread_pool = ThreadPoolExecutor(max_workers=1)
|
|
38
|
-
self.event_client = event_client
|
|
39
|
-
|
|
40
|
-
def _log(self, line: str, step_run_id: str | None):
|
|
41
|
-
try:
|
|
42
|
-
if not step_run_id:
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
self.event_client.log(message=line, step_run_id=step_run_id)
|
|
46
|
-
except Exception as e:
|
|
47
|
-
logger.error(f"Error logging: {e}")
|
|
48
|
-
|
|
49
|
-
def emit(self, record):
|
|
50
|
-
super().emit(record)
|
|
51
|
-
|
|
52
|
-
log_entry = self.format(record)
|
|
53
|
-
self.logger_thread_pool.submit(self._log, log_entry, record.step_run_id)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def capture_logs(
|
|
57
|
-
logger: logging.Logger,
|
|
58
|
-
event_client: EventClient,
|
|
59
|
-
func: Coroutine[Any, Any, Any],
|
|
60
|
-
):
|
|
61
|
-
@functools.wraps(func)
|
|
62
|
-
async def wrapper(*args, **kwargs):
|
|
63
|
-
if not logger:
|
|
64
|
-
raise Exception("No logger configured on client")
|
|
65
|
-
|
|
66
|
-
log_stream = StringIO()
|
|
67
|
-
custom_handler = CustomLogHandler(event_client, log_stream)
|
|
68
|
-
custom_handler.setLevel(logging.INFO)
|
|
69
|
-
custom_handler.addFilter(InjectingFilter())
|
|
70
|
-
logger.addHandler(custom_handler)
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
result = await func(*args, **kwargs)
|
|
74
|
-
finally:
|
|
75
|
-
custom_handler.flush()
|
|
76
|
-
logger.removeHandler(custom_handler)
|
|
77
|
-
log_stream.close()
|
|
78
|
-
|
|
79
|
-
return result
|
|
80
|
-
|
|
81
|
-
return wrapper
|