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.

Files changed (225) hide show
  1. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/METADATA +2 -3
  2. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/RECORD +4 -225
  3. hatchet_sdk/v0/__init__.py +0 -251
  4. hatchet_sdk/v0/client.py +0 -119
  5. hatchet_sdk/v0/clients/admin.py +0 -541
  6. hatchet_sdk/v0/clients/dispatcher/action_listener.py +0 -422
  7. hatchet_sdk/v0/clients/dispatcher/dispatcher.py +0 -204
  8. hatchet_sdk/v0/clients/event_ts.py +0 -28
  9. hatchet_sdk/v0/clients/events.py +0 -182
  10. hatchet_sdk/v0/clients/rest/__init__.py +0 -307
  11. hatchet_sdk/v0/clients/rest/api/__init__.py +0 -19
  12. hatchet_sdk/v0/clients/rest/api/api_token_api.py +0 -858
  13. hatchet_sdk/v0/clients/rest/api/default_api.py +0 -2259
  14. hatchet_sdk/v0/clients/rest/api/event_api.py +0 -2548
  15. hatchet_sdk/v0/clients/rest/api/github_api.py +0 -331
  16. hatchet_sdk/v0/clients/rest/api/healthcheck_api.py +0 -483
  17. hatchet_sdk/v0/clients/rest/api/log_api.py +0 -449
  18. hatchet_sdk/v0/clients/rest/api/metadata_api.py +0 -728
  19. hatchet_sdk/v0/clients/rest/api/rate_limits_api.py +0 -423
  20. hatchet_sdk/v0/clients/rest/api/slack_api.py +0 -577
  21. hatchet_sdk/v0/clients/rest/api/sns_api.py +0 -872
  22. hatchet_sdk/v0/clients/rest/api/step_run_api.py +0 -2202
  23. hatchet_sdk/v0/clients/rest/api/tenant_api.py +0 -4430
  24. hatchet_sdk/v0/clients/rest/api/user_api.py +0 -2888
  25. hatchet_sdk/v0/clients/rest/api/worker_api.py +0 -858
  26. hatchet_sdk/v0/clients/rest/api/workflow_api.py +0 -6312
  27. hatchet_sdk/v0/clients/rest/api/workflow_run_api.py +0 -1932
  28. hatchet_sdk/v0/clients/rest/api/workflow_runs_api.py +0 -610
  29. hatchet_sdk/v0/clients/rest/api_client.py +0 -759
  30. hatchet_sdk/v0/clients/rest/api_response.py +0 -22
  31. hatchet_sdk/v0/clients/rest/configuration.py +0 -611
  32. hatchet_sdk/v0/clients/rest/exceptions.py +0 -200
  33. hatchet_sdk/v0/clients/rest/models/__init__.py +0 -274
  34. hatchet_sdk/v0/clients/rest/models/accept_invite_request.py +0 -83
  35. hatchet_sdk/v0/clients/rest/models/api_error.py +0 -102
  36. hatchet_sdk/v0/clients/rest/models/api_errors.py +0 -100
  37. hatchet_sdk/v0/clients/rest/models/api_meta.py +0 -144
  38. hatchet_sdk/v0/clients/rest/models/api_meta_auth.py +0 -85
  39. hatchet_sdk/v0/clients/rest/models/api_meta_integration.py +0 -88
  40. hatchet_sdk/v0/clients/rest/models/api_meta_posthog.py +0 -90
  41. hatchet_sdk/v0/clients/rest/models/api_resource_meta.py +0 -98
  42. hatchet_sdk/v0/clients/rest/models/api_token.py +0 -105
  43. hatchet_sdk/v0/clients/rest/models/bulk_create_event_request.py +0 -100
  44. hatchet_sdk/v0/clients/rest/models/bulk_create_event_response.py +0 -110
  45. hatchet_sdk/v0/clients/rest/models/cancel_event_request.py +0 -85
  46. hatchet_sdk/v0/clients/rest/models/cancel_step_run_request.py +0 -83
  47. hatchet_sdk/v0/clients/rest/models/concurrency_limit_strategy.py +0 -39
  48. hatchet_sdk/v0/clients/rest/models/create_api_token_request.py +0 -92
  49. hatchet_sdk/v0/clients/rest/models/create_api_token_response.py +0 -83
  50. hatchet_sdk/v0/clients/rest/models/create_cron_workflow_trigger_request.py +0 -98
  51. hatchet_sdk/v0/clients/rest/models/create_event_request.py +0 -95
  52. hatchet_sdk/v0/clients/rest/models/create_pull_request_from_step_run.py +0 -83
  53. hatchet_sdk/v0/clients/rest/models/create_sns_integration_request.py +0 -85
  54. hatchet_sdk/v0/clients/rest/models/create_tenant_alert_email_group_request.py +0 -83
  55. hatchet_sdk/v0/clients/rest/models/create_tenant_invite_request.py +0 -86
  56. hatchet_sdk/v0/clients/rest/models/create_tenant_request.py +0 -84
  57. hatchet_sdk/v0/clients/rest/models/cron_workflows.py +0 -131
  58. hatchet_sdk/v0/clients/rest/models/cron_workflows_list.py +0 -110
  59. hatchet_sdk/v0/clients/rest/models/cron_workflows_method.py +0 -37
  60. hatchet_sdk/v0/clients/rest/models/cron_workflows_order_by_field.py +0 -37
  61. hatchet_sdk/v0/clients/rest/models/event.py +0 -143
  62. hatchet_sdk/v0/clients/rest/models/event_data.py +0 -83
  63. hatchet_sdk/v0/clients/rest/models/event_key_list.py +0 -98
  64. hatchet_sdk/v0/clients/rest/models/event_list.py +0 -110
  65. hatchet_sdk/v0/clients/rest/models/event_order_by_direction.py +0 -37
  66. hatchet_sdk/v0/clients/rest/models/event_order_by_field.py +0 -36
  67. hatchet_sdk/v0/clients/rest/models/event_update_cancel200_response.py +0 -85
  68. hatchet_sdk/v0/clients/rest/models/event_workflow_run_summary.py +0 -116
  69. hatchet_sdk/v0/clients/rest/models/events.py +0 -110
  70. hatchet_sdk/v0/clients/rest/models/get_step_run_diff_response.py +0 -100
  71. hatchet_sdk/v0/clients/rest/models/github_app_installation.py +0 -107
  72. hatchet_sdk/v0/clients/rest/models/github_branch.py +0 -86
  73. hatchet_sdk/v0/clients/rest/models/github_repo.py +0 -86
  74. hatchet_sdk/v0/clients/rest/models/info_get_version200_response.py +0 -83
  75. hatchet_sdk/v0/clients/rest/models/job.py +0 -132
  76. hatchet_sdk/v0/clients/rest/models/job_run.py +0 -176
  77. hatchet_sdk/v0/clients/rest/models/job_run_status.py +0 -41
  78. hatchet_sdk/v0/clients/rest/models/link_github_repository_request.py +0 -106
  79. hatchet_sdk/v0/clients/rest/models/list_api_tokens_response.py +0 -110
  80. hatchet_sdk/v0/clients/rest/models/list_github_app_installations_response.py +0 -112
  81. hatchet_sdk/v0/clients/rest/models/list_pull_requests_response.py +0 -100
  82. hatchet_sdk/v0/clients/rest/models/list_slack_webhooks.py +0 -110
  83. hatchet_sdk/v0/clients/rest/models/list_sns_integrations.py +0 -110
  84. hatchet_sdk/v0/clients/rest/models/log_line.py +0 -94
  85. hatchet_sdk/v0/clients/rest/models/log_line_level.py +0 -39
  86. hatchet_sdk/v0/clients/rest/models/log_line_list.py +0 -110
  87. hatchet_sdk/v0/clients/rest/models/log_line_order_by_direction.py +0 -37
  88. hatchet_sdk/v0/clients/rest/models/log_line_order_by_field.py +0 -36
  89. hatchet_sdk/v0/clients/rest/models/pagination_response.py +0 -95
  90. hatchet_sdk/v0/clients/rest/models/pull_request.py +0 -112
  91. hatchet_sdk/v0/clients/rest/models/pull_request_state.py +0 -37
  92. hatchet_sdk/v0/clients/rest/models/queue_metrics.py +0 -97
  93. hatchet_sdk/v0/clients/rest/models/rate_limit.py +0 -117
  94. hatchet_sdk/v0/clients/rest/models/rate_limit_list.py +0 -110
  95. hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_direction.py +0 -37
  96. hatchet_sdk/v0/clients/rest/models/rate_limit_order_by_field.py +0 -38
  97. hatchet_sdk/v0/clients/rest/models/recent_step_runs.py +0 -118
  98. hatchet_sdk/v0/clients/rest/models/reject_invite_request.py +0 -83
  99. hatchet_sdk/v0/clients/rest/models/replay_event_request.py +0 -85
  100. hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_request.py +0 -85
  101. hatchet_sdk/v0/clients/rest/models/replay_workflow_runs_response.py +0 -100
  102. hatchet_sdk/v0/clients/rest/models/rerun_step_run_request.py +0 -83
  103. hatchet_sdk/v0/clients/rest/models/schedule_workflow_run_request.py +0 -92
  104. hatchet_sdk/v0/clients/rest/models/scheduled_run_status.py +0 -42
  105. hatchet_sdk/v0/clients/rest/models/scheduled_workflows.py +0 -149
  106. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_list.py +0 -110
  107. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_method.py +0 -37
  108. hatchet_sdk/v0/clients/rest/models/scheduled_workflows_order_by_field.py +0 -37
  109. hatchet_sdk/v0/clients/rest/models/semaphore_slots.py +0 -113
  110. hatchet_sdk/v0/clients/rest/models/slack_webhook.py +0 -127
  111. hatchet_sdk/v0/clients/rest/models/sns_integration.py +0 -114
  112. hatchet_sdk/v0/clients/rest/models/step.py +0 -123
  113. hatchet_sdk/v0/clients/rest/models/step_run.py +0 -202
  114. hatchet_sdk/v0/clients/rest/models/step_run_archive.py +0 -142
  115. hatchet_sdk/v0/clients/rest/models/step_run_archive_list.py +0 -110
  116. hatchet_sdk/v0/clients/rest/models/step_run_diff.py +0 -91
  117. hatchet_sdk/v0/clients/rest/models/step_run_event.py +0 -122
  118. hatchet_sdk/v0/clients/rest/models/step_run_event_list.py +0 -110
  119. hatchet_sdk/v0/clients/rest/models/step_run_event_reason.py +0 -52
  120. hatchet_sdk/v0/clients/rest/models/step_run_event_severity.py +0 -38
  121. hatchet_sdk/v0/clients/rest/models/step_run_status.py +0 -44
  122. hatchet_sdk/v0/clients/rest/models/tenant.py +0 -118
  123. hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group.py +0 -98
  124. hatchet_sdk/v0/clients/rest/models/tenant_alert_email_group_list.py +0 -112
  125. hatchet_sdk/v0/clients/rest/models/tenant_alerting_settings.py +0 -143
  126. hatchet_sdk/v0/clients/rest/models/tenant_invite.py +0 -120
  127. hatchet_sdk/v0/clients/rest/models/tenant_invite_list.py +0 -110
  128. hatchet_sdk/v0/clients/rest/models/tenant_list.py +0 -110
  129. hatchet_sdk/v0/clients/rest/models/tenant_member.py +0 -123
  130. hatchet_sdk/v0/clients/rest/models/tenant_member_list.py +0 -110
  131. hatchet_sdk/v0/clients/rest/models/tenant_member_role.py +0 -38
  132. hatchet_sdk/v0/clients/rest/models/tenant_queue_metrics.py +0 -116
  133. hatchet_sdk/v0/clients/rest/models/tenant_resource.py +0 -40
  134. hatchet_sdk/v0/clients/rest/models/tenant_resource_limit.py +0 -135
  135. hatchet_sdk/v0/clients/rest/models/tenant_resource_policy.py +0 -102
  136. hatchet_sdk/v0/clients/rest/models/tenant_step_run_queue_metrics.py +0 -83
  137. hatchet_sdk/v0/clients/rest/models/trigger_workflow_run_request.py +0 -91
  138. hatchet_sdk/v0/clients/rest/models/update_tenant_alert_email_group_request.py +0 -83
  139. hatchet_sdk/v0/clients/rest/models/update_tenant_invite_request.py +0 -85
  140. hatchet_sdk/v0/clients/rest/models/update_tenant_request.py +0 -137
  141. hatchet_sdk/v0/clients/rest/models/update_worker_request.py +0 -87
  142. hatchet_sdk/v0/clients/rest/models/user.py +0 -126
  143. hatchet_sdk/v0/clients/rest/models/user_change_password_request.py +0 -88
  144. hatchet_sdk/v0/clients/rest/models/user_login_request.py +0 -86
  145. hatchet_sdk/v0/clients/rest/models/user_register_request.py +0 -91
  146. hatchet_sdk/v0/clients/rest/models/user_tenant_memberships_list.py +0 -110
  147. hatchet_sdk/v0/clients/rest/models/user_tenant_public.py +0 -86
  148. hatchet_sdk/v0/clients/rest/models/webhook_worker.py +0 -100
  149. hatchet_sdk/v0/clients/rest/models/webhook_worker_create_request.py +0 -94
  150. hatchet_sdk/v0/clients/rest/models/webhook_worker_create_response.py +0 -98
  151. hatchet_sdk/v0/clients/rest/models/webhook_worker_created.py +0 -102
  152. hatchet_sdk/v0/clients/rest/models/webhook_worker_list_response.py +0 -110
  153. hatchet_sdk/v0/clients/rest/models/webhook_worker_request.py +0 -102
  154. hatchet_sdk/v0/clients/rest/models/webhook_worker_request_list_response.py +0 -104
  155. hatchet_sdk/v0/clients/rest/models/webhook_worker_request_method.py +0 -38
  156. hatchet_sdk/v0/clients/rest/models/worker.py +0 -239
  157. hatchet_sdk/v0/clients/rest/models/worker_label.py +0 -102
  158. hatchet_sdk/v0/clients/rest/models/worker_list.py +0 -110
  159. hatchet_sdk/v0/clients/rest/models/worker_runtime_info.py +0 -103
  160. hatchet_sdk/v0/clients/rest/models/worker_runtime_sdks.py +0 -38
  161. hatchet_sdk/v0/clients/rest/models/worker_type.py +0 -38
  162. hatchet_sdk/v0/clients/rest/models/workflow.py +0 -165
  163. hatchet_sdk/v0/clients/rest/models/workflow_concurrency.py +0 -107
  164. hatchet_sdk/v0/clients/rest/models/workflow_deployment_config.py +0 -136
  165. hatchet_sdk/v0/clients/rest/models/workflow_kind.py +0 -38
  166. hatchet_sdk/v0/clients/rest/models/workflow_list.py +0 -120
  167. hatchet_sdk/v0/clients/rest/models/workflow_metrics.py +0 -97
  168. hatchet_sdk/v0/clients/rest/models/workflow_run.py +0 -188
  169. hatchet_sdk/v0/clients/rest/models/workflow_run_cancel200_response.py +0 -85
  170. hatchet_sdk/v0/clients/rest/models/workflow_run_list.py +0 -110
  171. hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_direction.py +0 -37
  172. hatchet_sdk/v0/clients/rest/models/workflow_run_order_by_field.py +0 -39
  173. hatchet_sdk/v0/clients/rest/models/workflow_run_shape.py +0 -186
  174. hatchet_sdk/v0/clients/rest/models/workflow_run_status.py +0 -42
  175. hatchet_sdk/v0/clients/rest/models/workflow_run_triggered_by.py +0 -112
  176. hatchet_sdk/v0/clients/rest/models/workflow_runs_cancel_request.py +0 -85
  177. hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics.py +0 -94
  178. hatchet_sdk/v0/clients/rest/models/workflow_runs_metrics_counts.py +0 -104
  179. hatchet_sdk/v0/clients/rest/models/workflow_tag.py +0 -84
  180. hatchet_sdk/v0/clients/rest/models/workflow_trigger_cron_ref.py +0 -86
  181. hatchet_sdk/v0/clients/rest/models/workflow_trigger_event_ref.py +0 -86
  182. hatchet_sdk/v0/clients/rest/models/workflow_triggers.py +0 -141
  183. hatchet_sdk/v0/clients/rest/models/workflow_update_request.py +0 -85
  184. hatchet_sdk/v0/clients/rest/models/workflow_version.py +0 -170
  185. hatchet_sdk/v0/clients/rest/models/workflow_version_concurrency.py +0 -114
  186. hatchet_sdk/v0/clients/rest/models/workflow_version_definition.py +0 -85
  187. hatchet_sdk/v0/clients/rest/models/workflow_version_meta.py +0 -123
  188. hatchet_sdk/v0/clients/rest/models/workflow_workers_count.py +0 -95
  189. hatchet_sdk/v0/clients/rest/rest.py +0 -187
  190. hatchet_sdk/v0/clients/rest/tenacity_utils.py +0 -39
  191. hatchet_sdk/v0/clients/rest_client.py +0 -622
  192. hatchet_sdk/v0/clients/run_event_listener.py +0 -260
  193. hatchet_sdk/v0/clients/workflow_listener.py +0 -277
  194. hatchet_sdk/v0/connection.py +0 -63
  195. hatchet_sdk/v0/context/__init__.py +0 -1
  196. hatchet_sdk/v0/context/context.py +0 -446
  197. hatchet_sdk/v0/context/worker_context.py +0 -28
  198. hatchet_sdk/v0/features/cron.py +0 -286
  199. hatchet_sdk/v0/features/scheduled.py +0 -248
  200. hatchet_sdk/v0/hatchet.py +0 -310
  201. hatchet_sdk/v0/labels.py +0 -10
  202. hatchet_sdk/v0/loader.py +0 -244
  203. hatchet_sdk/v0/metadata.py +0 -2
  204. hatchet_sdk/v0/opentelemetry/instrumentor.py +0 -393
  205. hatchet_sdk/v0/rate_limit.py +0 -126
  206. hatchet_sdk/v0/semver.py +0 -30
  207. hatchet_sdk/v0/token.py +0 -27
  208. hatchet_sdk/v0/utils/aio_utils.py +0 -137
  209. hatchet_sdk/v0/utils/backoff.py +0 -9
  210. hatchet_sdk/v0/utils/types.py +0 -8
  211. hatchet_sdk/v0/utils/typing.py +0 -12
  212. hatchet_sdk/v0/v2/callable.py +0 -202
  213. hatchet_sdk/v0/v2/concurrency.py +0 -47
  214. hatchet_sdk/v0/v2/hatchet.py +0 -224
  215. hatchet_sdk/v0/worker/__init__.py +0 -1
  216. hatchet_sdk/v0/worker/action_listener_process.py +0 -294
  217. hatchet_sdk/v0/worker/runner/run_loop_manager.py +0 -112
  218. hatchet_sdk/v0/worker/runner/runner.py +0 -460
  219. hatchet_sdk/v0/worker/runner/utils/capture_logs.py +0 -81
  220. hatchet_sdk/v0/worker/runner/utils/error_with_traceback.py +0 -6
  221. hatchet_sdk/v0/worker/worker.py +0 -391
  222. hatchet_sdk/v0/workflow.py +0 -261
  223. hatchet_sdk/v0/workflow_run.py +0 -59
  224. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/WHEEL +0 -0
  225. {hatchet_sdk-1.18.1.dist-info → hatchet_sdk-1.19.0.dist-info}/entry_points.txt +0 -0
@@ -1,391 +0,0 @@
1
- import asyncio
2
- import multiprocessing
3
- import multiprocessing.context
4
- import os
5
- import signal
6
- import sys
7
- from concurrent.futures import Future
8
- from dataclasses import dataclass, field
9
- from enum import Enum
10
- from multiprocessing import Queue
11
- from multiprocessing.process import BaseProcess
12
- from types import FrameType
13
- from typing import Any, Callable, TypeVar, get_type_hints
14
-
15
- from aiohttp import web
16
- from aiohttp.web_request import Request
17
- from aiohttp.web_response import Response
18
- from prometheus_client import CONTENT_TYPE_LATEST, Gauge, generate_latest
19
-
20
- from hatchet_sdk.contracts.workflows_pb2 import CreateWorkflowVersionOpts
21
- from hatchet_sdk.logger import logger
22
- from hatchet_sdk.v0 import Context
23
- from hatchet_sdk.v0.client import Client, new_client_raw
24
- from hatchet_sdk.v0.loader import ClientConfig
25
- from hatchet_sdk.v0.utils.types import WorkflowValidator
26
- from hatchet_sdk.v0.utils.typing import is_basemodel_subclass
27
- from hatchet_sdk.v0.v2.callable import HatchetCallable
28
- from hatchet_sdk.v0.v2.concurrency import ConcurrencyFunction
29
- from hatchet_sdk.v0.worker.action_listener_process import worker_action_listener_process
30
- from hatchet_sdk.v0.worker.runner.run_loop_manager import WorkerActionRunLoopManager
31
- from hatchet_sdk.v0.workflow import WorkflowInterface
32
-
33
- T = TypeVar("T")
34
-
35
-
36
- class WorkerStatus(Enum):
37
- INITIALIZED = 1
38
- STARTING = 2
39
- HEALTHY = 3
40
- UNHEALTHY = 4
41
-
42
-
43
- @dataclass
44
- class WorkerStartOptions:
45
- loop: asyncio.AbstractEventLoop | None = field(default=None)
46
-
47
-
48
- TWorkflow = TypeVar("TWorkflow", bound=object)
49
-
50
-
51
- class Worker:
52
- def __init__(
53
- self,
54
- name: str,
55
- config: ClientConfig = ClientConfig(),
56
- max_runs: int | None = None,
57
- labels: dict[str, str | int] = {},
58
- debug: bool = False,
59
- owned_loop: bool = True,
60
- handle_kill: bool = True,
61
- ) -> None:
62
- self.name = name
63
- self.config = config
64
- self.max_runs = max_runs
65
- self.debug = debug
66
- self.labels = labels
67
- self.handle_kill = handle_kill
68
- self.owned_loop = owned_loop
69
-
70
- self.client: Client
71
-
72
- self.action_registry: dict[str, Callable[[Context], Any]] = {}
73
- self.validator_registry: dict[str, WorkflowValidator] = {}
74
-
75
- self.killing: bool = False
76
- self._status: WorkerStatus
77
-
78
- self.action_listener_process: BaseProcess
79
- self.action_listener_health_check: asyncio.Task[Any]
80
- self.action_runner: WorkerActionRunLoopManager
81
-
82
- self.ctx = multiprocessing.get_context("spawn")
83
-
84
- self.action_queue: "Queue[Any]" = self.ctx.Queue()
85
- self.event_queue: "Queue[Any]" = self.ctx.Queue()
86
-
87
- self.loop: asyncio.AbstractEventLoop
88
-
89
- self.client = new_client_raw(self.config, self.debug)
90
- self.name = self.client.config.namespace + self.name
91
-
92
- self._setup_signal_handlers()
93
-
94
- self.worker_status_gauge = Gauge(
95
- "hatchet_worker_status", "Current status of the Hatchet worker"
96
- )
97
-
98
- def register_function(self, action: str, func: Callable[[Context], Any]) -> None:
99
- self.action_registry[action] = func
100
-
101
- def register_workflow_from_opts(
102
- self, name: str, opts: CreateWorkflowVersionOpts
103
- ) -> None:
104
- try:
105
- self.client.admin.put_workflow(opts.name, opts)
106
- except Exception as e:
107
- logger.error(f"failed to register workflow: {opts.name}")
108
- logger.error(e)
109
- sys.exit(1)
110
-
111
- def register_workflow(self, workflow: TWorkflow) -> None:
112
- ## Hack for typing
113
- assert isinstance(workflow, WorkflowInterface)
114
-
115
- namespace = self.client.config.namespace
116
-
117
- try:
118
- self.client.admin.put_workflow(
119
- workflow.get_name(namespace), workflow.get_create_opts(namespace)
120
- )
121
- except Exception as e:
122
- logger.error(f"failed to register workflow: {workflow.get_name(namespace)}")
123
- logger.error(e)
124
- sys.exit(1)
125
-
126
- def create_action_function(
127
- action_func: Callable[..., T]
128
- ) -> Callable[[Context], T]:
129
- def action_function(context: Context) -> T:
130
- return action_func(workflow, context)
131
-
132
- if asyncio.iscoroutinefunction(action_func):
133
- setattr(action_function, "is_coroutine", True)
134
- else:
135
- setattr(action_function, "is_coroutine", False)
136
-
137
- return action_function
138
-
139
- for action_name, action_func in workflow.get_actions(namespace):
140
- self.action_registry[action_name] = create_action_function(action_func)
141
- return_type = get_type_hints(action_func).get("return")
142
-
143
- self.validator_registry[action_name] = WorkflowValidator(
144
- workflow_input=workflow.input_validator,
145
- step_output=return_type if is_basemodel_subclass(return_type) else None,
146
- )
147
-
148
- def status(self) -> WorkerStatus:
149
- return self._status
150
-
151
- def setup_loop(self, loop: asyncio.AbstractEventLoop | None = None) -> bool:
152
- try:
153
- loop = loop or asyncio.get_running_loop()
154
- self.loop = loop
155
- created_loop = False
156
- logger.debug("using existing event loop")
157
- return created_loop
158
- except RuntimeError:
159
- self.loop = asyncio.new_event_loop()
160
- logger.debug("creating new event loop")
161
- asyncio.set_event_loop(self.loop)
162
- created_loop = True
163
- return created_loop
164
-
165
- async def health_check_handler(self, request: Request) -> Response:
166
- status = self.status()
167
-
168
- return web.json_response({"status": status.name})
169
-
170
- async def metrics_handler(self, request: Request) -> Response:
171
- self.worker_status_gauge.set(1 if self.status() == WorkerStatus.HEALTHY else 0)
172
-
173
- return web.Response(body=generate_latest(), content_type="text/plain")
174
-
175
- async def start_health_server(self) -> None:
176
- port = self.config.worker_healthcheck_port or 8001
177
-
178
- app = web.Application()
179
- app.add_routes(
180
- [
181
- web.get("/health", self.health_check_handler),
182
- web.get("/metrics", self.metrics_handler),
183
- ]
184
- )
185
-
186
- runner = web.AppRunner(app)
187
-
188
- try:
189
- await runner.setup()
190
- await web.TCPSite(runner, "0.0.0.0", port).start()
191
- except Exception as e:
192
- logger.error("failed to start healthcheck server")
193
- logger.error(str(e))
194
- return
195
-
196
- logger.info(f"healthcheck server running on port {port}")
197
-
198
- def start(
199
- self, options: WorkerStartOptions = WorkerStartOptions()
200
- ) -> Future[asyncio.Task[None]]:
201
- self.owned_loop = self.setup_loop(options.loop)
202
-
203
- f = asyncio.run_coroutine_threadsafe(
204
- self._async_start(options, _from_start=True), self.loop
205
- )
206
-
207
- # start the loop and wait until its closed
208
- if self.owned_loop:
209
- self.loop.run_forever()
210
-
211
- if self.handle_kill:
212
- sys.exit(0)
213
-
214
- return f
215
-
216
- ## Start methods
217
- async def _async_start(
218
- self,
219
- options: WorkerStartOptions = WorkerStartOptions(),
220
- _from_start: bool = False,
221
- ) -> asyncio.Task[None]:
222
- main_pid = os.getpid()
223
- logger.info("------------------------------------------")
224
- logger.info("STARTING HATCHET...")
225
- logger.debug(f"worker runtime starting on PID: {main_pid}")
226
-
227
- self._status = WorkerStatus.STARTING
228
-
229
- if len(self.action_registry.keys()) == 0:
230
- raise ValueError(
231
- "no actions registered, register workflows or actions before starting worker"
232
- )
233
-
234
- # non blocking setup
235
- if not _from_start:
236
- self.setup_loop(options.loop)
237
-
238
- if self.config.worker_healthcheck_enabled:
239
- await self.start_health_server()
240
-
241
- self.action_listener_process = self._start_listener()
242
-
243
- self.action_runner = self._run_action_runner()
244
-
245
- self.action_listener_health_check = self.loop.create_task(
246
- self._check_listener_health()
247
- )
248
-
249
- return await self.action_listener_health_check
250
-
251
- def _run_action_runner(self) -> WorkerActionRunLoopManager:
252
- # Retrieve the shared queue
253
- return WorkerActionRunLoopManager(
254
- self.name,
255
- self.action_registry,
256
- self.validator_registry,
257
- self.max_runs,
258
- self.config,
259
- self.action_queue,
260
- self.event_queue,
261
- self.loop,
262
- self.handle_kill,
263
- self.client.debug,
264
- self.labels,
265
- )
266
-
267
- def _start_listener(self) -> multiprocessing.context.SpawnProcess:
268
- action_list = [str(key) for key in self.action_registry.keys()]
269
-
270
- try:
271
- process = self.ctx.Process(
272
- target=worker_action_listener_process,
273
- args=(
274
- self.name,
275
- action_list,
276
- self.max_runs,
277
- self.config,
278
- self.action_queue,
279
- self.event_queue,
280
- self.handle_kill,
281
- self.client.debug,
282
- self.labels,
283
- ),
284
- )
285
- process.start()
286
- logger.debug(f"action listener starting on PID: {process.pid}")
287
-
288
- return process
289
- except Exception as e:
290
- logger.error(f"failed to start action listener: {e}")
291
- sys.exit(1)
292
-
293
- async def _check_listener_health(self) -> None:
294
- logger.debug("starting action listener health check...")
295
- try:
296
- while not self.killing:
297
- if (
298
- self.action_listener_process is None
299
- or not self.action_listener_process.is_alive()
300
- ):
301
- logger.debug("child action listener process killed...")
302
- self._status = WorkerStatus.UNHEALTHY
303
- if not self.killing:
304
- self.loop.create_task(self.exit_gracefully())
305
- break
306
- else:
307
- self._status = WorkerStatus.HEALTHY
308
- await asyncio.sleep(1)
309
- except Exception as e:
310
- logger.error(f"error checking listener health: {e}")
311
-
312
- ## Cleanup methods
313
- def _setup_signal_handlers(self) -> None:
314
- signal.signal(signal.SIGTERM, self._handle_exit_signal)
315
- signal.signal(signal.SIGINT, self._handle_exit_signal)
316
- signal.signal(signal.SIGQUIT, self._handle_force_quit_signal)
317
-
318
- def _handle_exit_signal(self, signum: int, frame: FrameType | None) -> None:
319
- sig_name = "SIGTERM" if signum == signal.SIGTERM else "SIGINT"
320
- logger.info(f"received signal {sig_name}...")
321
- self.loop.create_task(self.exit_gracefully())
322
-
323
- def _handle_force_quit_signal(self, signum: int, frame: FrameType | None) -> None:
324
- logger.info("received SIGQUIT...")
325
- self.exit_forcefully()
326
-
327
- async def close(self) -> None:
328
- logger.info(f"closing worker '{self.name}'...")
329
- self.killing = True
330
- # self.action_queue.close()
331
- # self.event_queue.close()
332
-
333
- if self.action_runner is not None:
334
- self.action_runner.cleanup()
335
-
336
- await self.action_listener_health_check
337
-
338
- async def exit_gracefully(self) -> None:
339
- logger.debug(f"gracefully stopping worker: {self.name}")
340
-
341
- if self.killing:
342
- return self.exit_forcefully()
343
-
344
- self.killing = True
345
-
346
- await self.action_runner.wait_for_tasks()
347
-
348
- await self.action_runner.exit_gracefully()
349
-
350
- if self.action_listener_process and self.action_listener_process.is_alive():
351
- self.action_listener_process.kill()
352
-
353
- await self.close()
354
- if self.loop and self.owned_loop:
355
- self.loop.stop()
356
-
357
- logger.info("👋")
358
-
359
- def exit_forcefully(self) -> None:
360
- self.killing = True
361
-
362
- logger.debug(f"forcefully stopping worker: {self.name}")
363
-
364
- self.close()
365
-
366
- if self.action_listener_process:
367
- self.action_listener_process.kill() # Forcefully kill the process
368
-
369
- logger.info("👋")
370
- sys.exit(
371
- 1
372
- ) # Exit immediately TODO - should we exit with 1 here, there may be other workers to cleanup
373
-
374
-
375
- def register_on_worker(callable: HatchetCallable[T], worker: Worker) -> None:
376
- worker.register_function(callable.get_action_name(), callable)
377
-
378
- if callable.function_on_failure is not None:
379
- worker.register_function(
380
- callable.function_on_failure.get_action_name(), callable.function_on_failure
381
- )
382
-
383
- if callable.function_concurrency is not None:
384
- worker.register_function(
385
- callable.function_concurrency.get_action_name(),
386
- callable.function_concurrency,
387
- )
388
-
389
- opts = callable.to_workflow_opts()
390
-
391
- worker.register_workflow_from_opts(opts.name, opts)
@@ -1,261 +0,0 @@
1
- import functools
2
- from typing import (
3
- Any,
4
- Callable,
5
- Protocol,
6
- Type,
7
- TypeVar,
8
- Union,
9
- cast,
10
- get_type_hints,
11
- runtime_checkable,
12
- )
13
-
14
- from pydantic import BaseModel
15
-
16
- from hatchet_sdk.contracts.workflows_pb2 import (
17
- CreateWorkflowJobOpts,
18
- CreateWorkflowStepOpts,
19
- CreateWorkflowVersionOpts,
20
- StickyStrategy,
21
- WorkflowConcurrencyOpts,
22
- WorkflowKind,
23
- )
24
- from hatchet_sdk.logger import logger
25
- from hatchet_sdk.v0 import ConcurrencyLimitStrategy
26
- from hatchet_sdk.v0.utils.typing import is_basemodel_subclass
27
-
28
-
29
- class WorkflowStepProtocol(Protocol):
30
- def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
31
-
32
- __name__: str
33
-
34
- _step_name: str
35
- _step_timeout: str | None
36
- _step_parents: list[str]
37
- _step_retries: int | None
38
- _step_rate_limits: list[str] | None
39
- _step_desired_worker_labels: dict[str, str]
40
- _step_backoff_factor: float | None
41
- _step_backoff_max_seconds: int | None
42
-
43
- _concurrency_fn_name: str
44
- _concurrency_max_runs: int | None
45
- _concurrency_limit_strategy: str | None
46
-
47
- _on_failure_step_name: str
48
- _on_failure_step_timeout: str | None
49
- _on_failure_step_retries: int
50
- _on_failure_step_rate_limits: list[str] | None
51
- _on_failure_step_backoff_factor: float | None
52
- _on_failure_step_backoff_max_seconds: int | None
53
-
54
-
55
- StepsType = list[tuple[str, WorkflowStepProtocol]]
56
-
57
- T = TypeVar("T")
58
- TW = TypeVar("TW", bound="WorkflowInterface")
59
-
60
-
61
- class ConcurrencyExpression:
62
- """
63
- Defines concurrency limits for a workflow using a CEL expression.
64
-
65
- Args:
66
- expression (str): CEL expression to determine concurrency grouping. (i.e. "input.user_id")
67
- max_runs (int): Maximum number of concurrent workflow runs.
68
- limit_strategy (ConcurrencyLimitStrategy): Strategy for handling limit violations.
69
-
70
- Example:
71
- ConcurrencyExpression("input.user_id", 5, ConcurrencyLimitStrategy.CANCEL_IN_PROGRESS)
72
- """
73
-
74
- def __init__(
75
- self, expression: str, max_runs: int, limit_strategy: ConcurrencyLimitStrategy
76
- ):
77
- self.expression = expression
78
- self.max_runs = max_runs
79
- self.limit_strategy = limit_strategy
80
-
81
-
82
- @runtime_checkable
83
- class WorkflowInterface(Protocol):
84
- def get_name(self, namespace: str) -> str: ...
85
-
86
- def get_actions(self, namespace: str) -> list[tuple[str, Callable[..., Any]]]: ...
87
-
88
- def get_create_opts(self, namespace: str) -> Any: ...
89
-
90
- on_events: list[str] | None
91
- on_crons: list[str] | None
92
- name: str
93
- version: str
94
- timeout: str
95
- schedule_timeout: str
96
- sticky: Union[StickyStrategy.Value, None] # type: ignore[name-defined]
97
- default_priority: int | None
98
- concurrency_expression: ConcurrencyExpression | None
99
- input_validator: Type[BaseModel] | None
100
-
101
-
102
- class WorkflowMeta(type):
103
- def __new__(
104
- cls: Type["WorkflowMeta"],
105
- name: str,
106
- bases: tuple[type, ...],
107
- attrs: dict[str, Any],
108
- ) -> "WorkflowMeta":
109
- def _create_steps_actions_list(name: str) -> StepsType:
110
- return [
111
- (getattr(func, name), attrs.pop(func_name))
112
- for func_name, func in list(attrs.items())
113
- if hasattr(func, name)
114
- ]
115
-
116
- concurrencyActions = _create_steps_actions_list("_concurrency_fn_name")
117
- steps = _create_steps_actions_list("_step_name")
118
-
119
- onFailureSteps = _create_steps_actions_list("_on_failure_step_name")
120
-
121
- # Define __init__ and get_step_order methods
122
- original_init = attrs.get("__init__") # Get the original __init__ if it exists
123
-
124
- def __init__(self: TW, *args: Any, **kwargs: Any) -> None:
125
- if original_init:
126
- original_init(self, *args, **kwargs) # Call original __init__
127
-
128
- def get_service_name(namespace: str) -> str:
129
- return f"{namespace}{name.lower()}"
130
-
131
- @functools.cache
132
- def get_actions(self: TW, namespace: str) -> StepsType:
133
- serviceName = get_service_name(namespace)
134
-
135
- func_actions = [
136
- (serviceName + ":" + func_name, func) for func_name, func in steps
137
- ]
138
- concurrency_actions = [
139
- (serviceName + ":" + func_name, func)
140
- for func_name, func in concurrencyActions
141
- ]
142
- onFailure_actions = [
143
- (serviceName + ":" + func_name, func)
144
- for func_name, func in onFailureSteps
145
- ]
146
-
147
- return func_actions + concurrency_actions + onFailure_actions
148
-
149
- # Add these methods and steps to class attributes
150
- attrs["__init__"] = __init__
151
- attrs["get_actions"] = get_actions
152
-
153
- for step_name, step_func in steps:
154
- attrs[step_name] = step_func
155
-
156
- def get_name(self: TW, namespace: str) -> str:
157
- return namespace + cast(str, attrs["name"])
158
-
159
- attrs["get_name"] = get_name
160
-
161
- cron_triggers = attrs["on_crons"]
162
- version = attrs["version"]
163
- schedule_timeout = attrs["schedule_timeout"]
164
- sticky = attrs["sticky"]
165
- default_priority = attrs["default_priority"]
166
-
167
- @functools.cache
168
- def get_create_opts(self: TW, namespace: str) -> CreateWorkflowVersionOpts:
169
- serviceName = get_service_name(namespace)
170
- name = self.get_name(namespace)
171
- event_triggers = [namespace + event for event in attrs["on_events"]]
172
- createStepOpts: list[CreateWorkflowStepOpts] = [
173
- CreateWorkflowStepOpts(
174
- readable_id=step_name,
175
- action=serviceName + ":" + step_name,
176
- timeout=func._step_timeout or "60s",
177
- inputs="{}",
178
- parents=[x for x in func._step_parents],
179
- retries=func._step_retries,
180
- rate_limits=func._step_rate_limits, # type: ignore[arg-type]
181
- worker_labels=func._step_desired_worker_labels, # type: ignore[arg-type]
182
- backoff_factor=func._step_backoff_factor,
183
- backoff_max_seconds=func._step_backoff_max_seconds,
184
- )
185
- for step_name, func in steps
186
- ]
187
-
188
- concurrency: WorkflowConcurrencyOpts | None = None
189
-
190
- if len(concurrencyActions) > 0:
191
- action = concurrencyActions[0]
192
-
193
- concurrency = WorkflowConcurrencyOpts(
194
- action=serviceName + ":" + action[0],
195
- max_runs=action[1]._concurrency_max_runs,
196
- limit_strategy=action[1]._concurrency_limit_strategy,
197
- )
198
-
199
- if self.concurrency_expression:
200
- concurrency = WorkflowConcurrencyOpts(
201
- expression=self.concurrency_expression.expression,
202
- max_runs=self.concurrency_expression.max_runs,
203
- limit_strategy=self.concurrency_expression.limit_strategy,
204
- )
205
-
206
- if len(concurrencyActions) > 0 and self.concurrency_expression:
207
- raise ValueError(
208
- "Error: Both concurrencyActions and concurrency_expression are defined. Please use only one concurrency configuration method."
209
- )
210
-
211
- on_failure_job: CreateWorkflowJobOpts | None = None
212
-
213
- if len(onFailureSteps) > 0:
214
- func_name, func = onFailureSteps[0]
215
- on_failure_job = CreateWorkflowJobOpts(
216
- name=name + "-on-failure",
217
- steps=[
218
- CreateWorkflowStepOpts(
219
- readable_id=func_name,
220
- action=serviceName + ":" + func_name,
221
- timeout=func._on_failure_step_timeout or "60s",
222
- inputs="{}",
223
- parents=[],
224
- retries=func._on_failure_step_retries,
225
- rate_limits=func._on_failure_step_rate_limits, # type: ignore[arg-type]
226
- backoff_factor=func._on_failure_step_backoff_factor,
227
- backoff_max_seconds=func._on_failure_step_backoff_max_seconds,
228
- )
229
- ],
230
- )
231
-
232
- validated_priority = (
233
- max(1, min(3, default_priority)) if default_priority else None
234
- )
235
- if validated_priority != default_priority:
236
- logger.warning(
237
- "Warning: Default Priority Must be between 1 and 3 -- inclusively. Adjusted to be within the range."
238
- )
239
-
240
- return CreateWorkflowVersionOpts(
241
- name=name,
242
- kind=WorkflowKind.DAG,
243
- version=version,
244
- event_triggers=event_triggers,
245
- cron_triggers=cron_triggers,
246
- schedule_timeout=schedule_timeout,
247
- sticky=sticky,
248
- jobs=[
249
- CreateWorkflowJobOpts(
250
- name=name,
251
- steps=createStepOpts,
252
- )
253
- ],
254
- on_failure_job=on_failure_job,
255
- concurrency=concurrency,
256
- default_priority=validated_priority,
257
- )
258
-
259
- attrs["get_create_opts"] = get_create_opts
260
-
261
- return super(WorkflowMeta, cls).__new__(cls, name, bases, attrs)
@@ -1,59 +0,0 @@
1
- import asyncio
2
- from typing import Any, Coroutine, Generic, Optional, TypedDict, TypeVar
3
-
4
- from hatchet_sdk.v0.clients.run_event_listener import (
5
- RunEventListener,
6
- RunEventListenerClient,
7
- )
8
- from hatchet_sdk.v0.clients.workflow_listener import PooledWorkflowRunListener
9
- from hatchet_sdk.v0.utils.aio_utils import EventLoopThread, get_active_event_loop
10
-
11
-
12
- class WorkflowRunRef:
13
- workflow_run_id: str
14
-
15
- def __init__(
16
- self,
17
- workflow_run_id: str,
18
- workflow_listener: PooledWorkflowRunListener,
19
- workflow_run_event_listener: RunEventListenerClient,
20
- ):
21
- self.workflow_run_id = workflow_run_id
22
- self.workflow_listener = workflow_listener
23
- self.workflow_run_event_listener = workflow_run_event_listener
24
-
25
- def __str__(self):
26
- return self.workflow_run_id
27
-
28
- def stream(self) -> RunEventListener:
29
- return self.workflow_run_event_listener.stream(self.workflow_run_id)
30
-
31
- def result(self) -> Coroutine:
32
- return self.workflow_listener.result(self.workflow_run_id)
33
-
34
- def sync_result(self) -> dict:
35
- coro = self.workflow_listener.result(self.workflow_run_id)
36
- loop = get_active_event_loop()
37
-
38
- if loop is None:
39
- loop = asyncio.new_event_loop()
40
- asyncio.set_event_loop(loop)
41
- try:
42
- return loop.run_until_complete(coro)
43
- finally:
44
- asyncio.set_event_loop(None)
45
- else:
46
- return loop.run_until_complete(coro)
47
-
48
-
49
- T = TypeVar("T")
50
-
51
-
52
- class RunRef(WorkflowRunRef, Generic[T]):
53
- async def result(self) -> T:
54
- res = await self.workflow_listener.result(self.workflow_run_id)
55
-
56
- if len(res) == 1:
57
- return list(res.values())[0]
58
-
59
- return res