hatchet-sdk 1.18.1__py3-none-any.whl → 1.20.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.

Files changed (234) hide show
  1. hatchet_sdk/clients/dispatcher/action_listener.py +0 -1
  2. hatchet_sdk/clients/dispatcher/dispatcher.py +0 -30
  3. hatchet_sdk/hatchet.py +0 -20
  4. hatchet_sdk/opentelemetry/instrumentor.py +1 -27
  5. hatchet_sdk/runnables/action.py +2 -5
  6. hatchet_sdk/runnables/task.py +0 -1
  7. hatchet_sdk/utils/opentelemetry.py +0 -1
  8. hatchet_sdk/worker/action_listener_process.py +0 -29
  9. hatchet_sdk/worker/runner/runner.py +1 -105
  10. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.20.0.dist-info}/METADATA +2 -3
  11. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.20.0.dist-info}/RECORD +13 -234
  12. hatchet_sdk/v0/__init__.py +0 -251
  13. hatchet_sdk/v0/client.py +0 -119
  14. hatchet_sdk/v0/clients/admin.py +0 -541
  15. hatchet_sdk/v0/clients/dispatcher/action_listener.py +0 -422
  16. hatchet_sdk/v0/clients/dispatcher/dispatcher.py +0 -204
  17. hatchet_sdk/v0/clients/event_ts.py +0 -28
  18. hatchet_sdk/v0/clients/events.py +0 -182
  19. hatchet_sdk/v0/clients/rest/__init__.py +0 -307
  20. hatchet_sdk/v0/clients/rest/api/__init__.py +0 -19
  21. hatchet_sdk/v0/clients/rest/api/api_token_api.py +0 -858
  22. hatchet_sdk/v0/clients/rest/api/default_api.py +0 -2259
  23. hatchet_sdk/v0/clients/rest/api/event_api.py +0 -2548
  24. hatchet_sdk/v0/clients/rest/api/github_api.py +0 -331
  25. hatchet_sdk/v0/clients/rest/api/healthcheck_api.py +0 -483
  26. hatchet_sdk/v0/clients/rest/api/log_api.py +0 -449
  27. hatchet_sdk/v0/clients/rest/api/metadata_api.py +0 -728
  28. hatchet_sdk/v0/clients/rest/api/rate_limits_api.py +0 -423
  29. hatchet_sdk/v0/clients/rest/api/slack_api.py +0 -577
  30. hatchet_sdk/v0/clients/rest/api/sns_api.py +0 -872
  31. hatchet_sdk/v0/clients/rest/api/step_run_api.py +0 -2202
  32. hatchet_sdk/v0/clients/rest/api/tenant_api.py +0 -4430
  33. hatchet_sdk/v0/clients/rest/api/user_api.py +0 -2888
  34. hatchet_sdk/v0/clients/rest/api/worker_api.py +0 -858
  35. hatchet_sdk/v0/clients/rest/api/workflow_api.py +0 -6312
  36. hatchet_sdk/v0/clients/rest/api/workflow_run_api.py +0 -1932
  37. hatchet_sdk/v0/clients/rest/api/workflow_runs_api.py +0 -610
  38. hatchet_sdk/v0/clients/rest/api_client.py +0 -759
  39. hatchet_sdk/v0/clients/rest/api_response.py +0 -22
  40. hatchet_sdk/v0/clients/rest/configuration.py +0 -611
  41. hatchet_sdk/v0/clients/rest/exceptions.py +0 -200
  42. hatchet_sdk/v0/clients/rest/models/__init__.py +0 -274
  43. hatchet_sdk/v0/clients/rest/models/accept_invite_request.py +0 -83
  44. hatchet_sdk/v0/clients/rest/models/api_error.py +0 -102
  45. hatchet_sdk/v0/clients/rest/models/api_errors.py +0 -100
  46. hatchet_sdk/v0/clients/rest/models/api_meta.py +0 -144
  47. hatchet_sdk/v0/clients/rest/models/api_meta_auth.py +0 -85
  48. hatchet_sdk/v0/clients/rest/models/api_meta_integration.py +0 -88
  49. hatchet_sdk/v0/clients/rest/models/api_meta_posthog.py +0 -90
  50. hatchet_sdk/v0/clients/rest/models/api_resource_meta.py +0 -98
  51. hatchet_sdk/v0/clients/rest/models/api_token.py +0 -105
  52. hatchet_sdk/v0/clients/rest/models/bulk_create_event_request.py +0 -100
  53. hatchet_sdk/v0/clients/rest/models/bulk_create_event_response.py +0 -110
  54. hatchet_sdk/v0/clients/rest/models/cancel_event_request.py +0 -85
  55. hatchet_sdk/v0/clients/rest/models/cancel_step_run_request.py +0 -83
  56. hatchet_sdk/v0/clients/rest/models/concurrency_limit_strategy.py +0 -39
  57. hatchet_sdk/v0/clients/rest/models/create_api_token_request.py +0 -92
  58. hatchet_sdk/v0/clients/rest/models/create_api_token_response.py +0 -83
  59. hatchet_sdk/v0/clients/rest/models/create_cron_workflow_trigger_request.py +0 -98
  60. hatchet_sdk/v0/clients/rest/models/create_event_request.py +0 -95
  61. hatchet_sdk/v0/clients/rest/models/create_pull_request_from_step_run.py +0 -83
  62. hatchet_sdk/v0/clients/rest/models/create_sns_integration_request.py +0 -85
  63. hatchet_sdk/v0/clients/rest/models/create_tenant_alert_email_group_request.py +0 -83
  64. hatchet_sdk/v0/clients/rest/models/create_tenant_invite_request.py +0 -86
  65. hatchet_sdk/v0/clients/rest/models/create_tenant_request.py +0 -84
  66. hatchet_sdk/v0/clients/rest/models/cron_workflows.py +0 -131
  67. hatchet_sdk/v0/clients/rest/models/cron_workflows_list.py +0 -110
  68. hatchet_sdk/v0/clients/rest/models/cron_workflows_method.py +0 -37
  69. hatchet_sdk/v0/clients/rest/models/cron_workflows_order_by_field.py +0 -37
  70. hatchet_sdk/v0/clients/rest/models/event.py +0 -143
  71. hatchet_sdk/v0/clients/rest/models/event_data.py +0 -83
  72. hatchet_sdk/v0/clients/rest/models/event_key_list.py +0 -98
  73. hatchet_sdk/v0/clients/rest/models/event_list.py +0 -110
  74. hatchet_sdk/v0/clients/rest/models/event_order_by_direction.py +0 -37
  75. hatchet_sdk/v0/clients/rest/models/event_order_by_field.py +0 -36
  76. hatchet_sdk/v0/clients/rest/models/event_update_cancel200_response.py +0 -85
  77. hatchet_sdk/v0/clients/rest/models/event_workflow_run_summary.py +0 -116
  78. hatchet_sdk/v0/clients/rest/models/events.py +0 -110
  79. hatchet_sdk/v0/clients/rest/models/get_step_run_diff_response.py +0 -100
  80. hatchet_sdk/v0/clients/rest/models/github_app_installation.py +0 -107
  81. hatchet_sdk/v0/clients/rest/models/github_branch.py +0 -86
  82. hatchet_sdk/v0/clients/rest/models/github_repo.py +0 -86
  83. hatchet_sdk/v0/clients/rest/models/info_get_version200_response.py +0 -83
  84. hatchet_sdk/v0/clients/rest/models/job.py +0 -132
  85. hatchet_sdk/v0/clients/rest/models/job_run.py +0 -176
  86. hatchet_sdk/v0/clients/rest/models/job_run_status.py +0 -41
  87. hatchet_sdk/v0/clients/rest/models/link_github_repository_request.py +0 -106
  88. hatchet_sdk/v0/clients/rest/models/list_api_tokens_response.py +0 -110
  89. hatchet_sdk/v0/clients/rest/models/list_github_app_installations_response.py +0 -112
  90. hatchet_sdk/v0/clients/rest/models/list_pull_requests_response.py +0 -100
  91. hatchet_sdk/v0/clients/rest/models/list_slack_webhooks.py +0 -110
  92. hatchet_sdk/v0/clients/rest/models/list_sns_integrations.py +0 -110
  93. hatchet_sdk/v0/clients/rest/models/log_line.py +0 -94
  94. hatchet_sdk/v0/clients/rest/models/log_line_level.py +0 -39
  95. hatchet_sdk/v0/clients/rest/models/log_line_list.py +0 -110
  96. hatchet_sdk/v0/clients/rest/models/log_line_order_by_direction.py +0 -37
  97. hatchet_sdk/v0/clients/rest/models/log_line_order_by_field.py +0 -36
  98. hatchet_sdk/v0/clients/rest/models/pagination_response.py +0 -95
  99. hatchet_sdk/v0/clients/rest/models/pull_request.py +0 -112
  100. hatchet_sdk/v0/clients/rest/models/pull_request_state.py +0 -37
  101. hatchet_sdk/v0/clients/rest/models/queue_metrics.py +0 -97
  102. hatchet_sdk/v0/clients/rest/models/rate_limit.py +0 -117
  103. hatchet_sdk/v0/clients/rest/models/rate_limit_list.py +0 -110
  104. hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_direction.py +0 -37
  105. hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_field.py +0 -38
  106. hatchet_sdk/v0/clients/rest/models/recent_step_runs.py +0 -118
  107. hatchet_sdk/v0/clients/rest/models/reject_invite_request.py +0 -83
  108. hatchet_sdk/v0/clients/rest/models/replay_event_request.py +0 -85
  109. hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_request.py +0 -85
  110. hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_response.py +0 -100
  111. hatchet_sdk/v0/clients/rest/models/rerun_step_run_request.py +0 -83
  112. hatchet_sdk/v0/clients/rest/models/schedule_workflow_run_request.py +0 -92
  113. hatchet_sdk/v0/clients/rest/models/scheduled_run_status.py +0 -42
  114. hatchet_sdk/v0/clients/rest/models/scheduled_workflows.py +0 -149
  115. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_list.py +0 -110
  116. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_method.py +0 -37
  117. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_order_by_field.py +0 -37
  118. hatchet_sdk/v0/clients/rest/models/semaphore_slots.py +0 -113
  119. hatchet_sdk/v0/clients/rest/models/slack_webhook.py +0 -127
  120. hatchet_sdk/v0/clients/rest/models/sns_integration.py +0 -114
  121. hatchet_sdk/v0/clients/rest/models/step.py +0 -123
  122. hatchet_sdk/v0/clients/rest/models/step_run.py +0 -202
  123. hatchet_sdk/v0/clients/rest/models/step_run_archive.py +0 -142
  124. hatchet_sdk/v0/clients/rest/models/step_run_archive_list.py +0 -110
  125. hatchet_sdk/v0/clients/rest/models/step_run_diff.py +0 -91
  126. hatchet_sdk/v0/clients/rest/models/step_run_event.py +0 -122
  127. hatchet_sdk/v0/clients/rest/models/step_run_event_list.py +0 -110
  128. hatchet_sdk/v0/clients/rest/models/step_run_event_reason.py +0 -52
  129. hatchet_sdk/v0/clients/rest/models/step_run_event_severity.py +0 -38
  130. hatchet_sdk/v0/clients/rest/models/step_run_status.py +0 -44
  131. hatchet_sdk/v0/clients/rest/models/tenant.py +0 -118
  132. hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group.py +0 -98
  133. hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group_list.py +0 -112
  134. hatchet_sdk/v0/clients/rest/models/tenant_alerting_settings.py +0 -143
  135. hatchet_sdk/v0/clients/rest/models/tenant_invite.py +0 -120
  136. hatchet_sdk/v0/clients/rest/models/tenant_invite_list.py +0 -110
  137. hatchet_sdk/v0/clients/rest/models/tenant_list.py +0 -110
  138. hatchet_sdk/v0/clients/rest/models/tenant_member.py +0 -123
  139. hatchet_sdk/v0/clients/rest/models/tenant_member_list.py +0 -110
  140. hatchet_sdk/v0/clients/rest/models/tenant_member_role.py +0 -38
  141. hatchet_sdk/v0/clients/rest/models/tenant_queue_metrics.py +0 -116
  142. hatchet_sdk/v0/clients/rest/models/tenant_resource.py +0 -40
  143. hatchet_sdk/v0/clients/rest/models/tenant_resource_limit.py +0 -135
  144. hatchet_sdk/v0/clients/rest/models/tenant_resource_policy.py +0 -102
  145. hatchet_sdk/v0/clients/rest/models/tenant_step_run_queue_metrics.py +0 -83
  146. hatchet_sdk/v0/clients/rest/models/trigger_workflow_run_request.py +0 -91
  147. hatchet_sdk/v0/clients/rest/models/update_tenant_alert_email_group_request.py +0 -83
  148. hatchet_sdk/v0/clients/rest/models/update_tenant_invite_request.py +0 -85
  149. hatchet_sdk/v0/clients/rest/models/update_tenant_request.py +0 -137
  150. hatchet_sdk/v0/clients/rest/models/update_worker_request.py +0 -87
  151. hatchet_sdk/v0/clients/rest/models/user.py +0 -126
  152. hatchet_sdk/v0/clients/rest/models/user_change_password_request.py +0 -88
  153. hatchet_sdk/v0/clients/rest/models/user_login_request.py +0 -86
  154. hatchet_sdk/v0/clients/rest/models/user_register_request.py +0 -91
  155. hatchet_sdk/v0/clients/rest/models/user_tenant_memberships_list.py +0 -110
  156. hatchet_sdk/v0/clients/rest/models/user_tenant_public.py +0 -86
  157. hatchet_sdk/v0/clients/rest/models/webhook_worker.py +0 -100
  158. hatchet_sdk/v0/clients/rest/models/webhook_worker_create_request.py +0 -94
  159. hatchet_sdk/v0/clients/rest/models/webhook_worker_create_response.py +0 -98
  160. hatchet_sdk/v0/clients/rest/models/webhook_worker_created.py +0 -102
  161. hatchet_sdk/v0/clients/rest/models/webhook_worker_list_response.py +0 -110
  162. hatchet_sdk/v0/clients/rest/models/webhook_worker_request.py +0 -102
  163. hatchet_sdk/v0/clients/rest/models/webhook_worker_request_list_response.py +0 -104
  164. hatchet_sdk/v0/clients/rest/models/webhook_worker_request_method.py +0 -38
  165. hatchet_sdk/v0/clients/rest/models/worker.py +0 -239
  166. hatchet_sdk/v0/clients/rest/models/worker_label.py +0 -102
  167. hatchet_sdk/v0/clients/rest/models/worker_list.py +0 -110
  168. hatchet_sdk/v0/clients/rest/models/worker_runtime_info.py +0 -103
  169. hatchet_sdk/v0/clients/rest/models/worker_runtime_sdks.py +0 -38
  170. hatchet_sdk/v0/clients/rest/models/worker_type.py +0 -38
  171. hatchet_sdk/v0/clients/rest/models/workflow.py +0 -165
  172. hatchet_sdk/v0/clients/rest/models/workflow_concurrency.py +0 -107
  173. hatchet_sdk/v0/clients/rest/models/workflow_deployment_config.py +0 -136
  174. hatchet_sdk/v0/clients/rest/models/workflow_kind.py +0 -38
  175. hatchet_sdk/v0/clients/rest/models/workflow_list.py +0 -120
  176. hatchet_sdk/v0/clients/rest/models/workflow_metrics.py +0 -97
  177. hatchet_sdk/v0/clients/rest/models/workflow_run.py +0 -188
  178. hatchet_sdk/v0/clients/rest/models/workflow_run_cancel200_response.py +0 -85
  179. hatchet_sdk/v0/clients/rest/models/workflow_run_list.py +0 -110
  180. hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_direction.py +0 -37
  181. hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_field.py +0 -39
  182. hatchet_sdk/v0/clients/rest/models/workflow_run_shape.py +0 -186
  183. hatchet_sdk/v0/clients/rest/models/workflow_run_status.py +0 -42
  184. hatchet_sdk/v0/clients/rest/models/workflow_run_triggered_by.py +0 -112
  185. hatchet_sdk/v0/clients/rest/models/workflow_runs_cancel_request.py +0 -85
  186. hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics.py +0 -94
  187. hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics_counts.py +0 -104
  188. hatchet_sdk/v0/clients/rest/models/workflow_tag.py +0 -84
  189. hatchet_sdk/v0/clients/rest/models/workflow_trigger_cron_ref.py +0 -86
  190. hatchet_sdk/v0/clients/rest/models/workflow_trigger_event_ref.py +0 -86
  191. hatchet_sdk/v0/clients/rest/models/workflow_triggers.py +0 -141
  192. hatchet_sdk/v0/clients/rest/models/workflow_update_request.py +0 -85
  193. hatchet_sdk/v0/clients/rest/models/workflow_version.py +0 -170
  194. hatchet_sdk/v0/clients/rest/models/workflow_version_concurrency.py +0 -114
  195. hatchet_sdk/v0/clients/rest/models/workflow_version_definition.py +0 -85
  196. hatchet_sdk/v0/clients/rest/models/workflow_version_meta.py +0 -123
  197. hatchet_sdk/v0/clients/rest/models/workflow_workers_count.py +0 -95
  198. hatchet_sdk/v0/clients/rest/rest.py +0 -187
  199. hatchet_sdk/v0/clients/rest/tenacity_utils.py +0 -39
  200. hatchet_sdk/v0/clients/rest_client.py +0 -622
  201. hatchet_sdk/v0/clients/run_event_listener.py +0 -260
  202. hatchet_sdk/v0/clients/workflow_listener.py +0 -277
  203. hatchet_sdk/v0/connection.py +0 -63
  204. hatchet_sdk/v0/context/__init__.py +0 -1
  205. hatchet_sdk/v0/context/context.py +0 -446
  206. hatchet_sdk/v0/context/worker_context.py +0 -28
  207. hatchet_sdk/v0/features/cron.py +0 -286
  208. hatchet_sdk/v0/features/scheduled.py +0 -248
  209. hatchet_sdk/v0/hatchet.py +0 -310
  210. hatchet_sdk/v0/labels.py +0 -10
  211. hatchet_sdk/v0/loader.py +0 -244
  212. hatchet_sdk/v0/metadata.py +0 -2
  213. hatchet_sdk/v0/opentelemetry/instrumentor.py +0 -393
  214. hatchet_sdk/v0/rate_limit.py +0 -126
  215. hatchet_sdk/v0/semver.py +0 -30
  216. hatchet_sdk/v0/token.py +0 -27
  217. hatchet_sdk/v0/utils/aio_utils.py +0 -137
  218. hatchet_sdk/v0/utils/backoff.py +0 -9
  219. hatchet_sdk/v0/utils/types.py +0 -8
  220. hatchet_sdk/v0/utils/typing.py +0 -12
  221. hatchet_sdk/v0/v2/callable.py +0 -202
  222. hatchet_sdk/v0/v2/concurrency.py +0 -47
  223. hatchet_sdk/v0/v2/hatchet.py +0 -224
  224. hatchet_sdk/v0/worker/__init__.py +0 -1
  225. hatchet_sdk/v0/worker/action_listener_process.py +0 -294
  226. hatchet_sdk/v0/worker/runner/run_loop_manager.py +0 -112
  227. hatchet_sdk/v0/worker/runner/runner.py +0 -460
  228. hatchet_sdk/v0/worker/runner/utils/capture_logs.py +0 -81
  229. hatchet_sdk/v0/worker/runner/utils/error_with_traceback.py +0 -6
  230. hatchet_sdk/v0/worker/worker.py +0 -391
  231. hatchet_sdk/v0/workflow.py +0 -261
  232. hatchet_sdk/v0/workflow_run.py +0 -59
  233. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.20.0.dist-info}/WHEEL +0 -0
  234. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.20.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
@@ -1,6 +0,0 @@
1
- import traceback
2
-
3
-
4
- def errorWithTraceback(message: str, e: Exception):
5
- trace = "".join(traceback.format_exception(type(e), e, e.__traceback__))
6
- return f"{message}\n{trace}"