atlan-application-sdk 2.1.1__py3-none-any.whl → 2.3.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.
application_sdk/worker.py CHANGED
@@ -1,10 +1,11 @@
1
1
  """Worker module for managing Temporal workers.
2
2
 
3
3
  This module provides the Worker class for managing Temporal workflow workers,
4
- including their initialization, configuration, and execution.
4
+ including their initialization, configuration, and execution with graceful shutdown support.
5
5
  """
6
6
 
7
7
  import asyncio
8
+ import signal
8
9
  import sys
9
10
  import threading
10
11
  from concurrent.futures import ThreadPoolExecutor
@@ -14,7 +15,11 @@ from temporalio.types import CallableType, ClassType
14
15
  from temporalio.worker import Worker as TemporalWorker
15
16
 
16
17
  from application_sdk.clients.workflow import WorkflowClient
17
- from application_sdk.constants import DEPLOYMENT_NAME, MAX_CONCURRENT_ACTIVITIES
18
+ from application_sdk.constants import (
19
+ DEPLOYMENT_NAME,
20
+ GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS,
21
+ MAX_CONCURRENT_ACTIVITIES,
22
+ )
18
23
  from application_sdk.interceptors.models import (
19
24
  ApplicationEventNames,
20
25
  Event,
@@ -37,13 +42,16 @@ if sys.platform not in ("win32", "cygwin"):
37
42
  # uvloop is not available, use default asyncio
38
43
  logger.warning("uvloop is not available, using default asyncio")
39
44
  pass
45
+ elif sys.platform == "win32":
46
+ # Use WindowsSelectorEventLoopPolicy for Windows platform
47
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
40
48
 
41
49
 
42
50
  class Worker:
43
51
  """Worker class for managing Temporal workflow workers.
44
52
 
45
53
  This class handles the initialization and execution of Temporal workers,
46
- including their activities, workflows, and module configurations.
54
+ including their activities, workflows, module configurations, and graceful shutdown.
47
55
 
48
56
  Attributes:
49
57
  workflow_client: Client for interacting with Temporal.
@@ -53,6 +61,13 @@ class Worker:
53
61
  passthrough_modules: List of module names to pass through.
54
62
  max_concurrent_activities: Maximum number of concurrent activities.
55
63
 
64
+ Graceful Shutdown:
65
+ When SIGTERM or SIGINT is received:
66
+ 1. Signal handlers trigger worker.shutdown()
67
+ 2. Worker stops polling for new tasks
68
+ 3. In-flight activities are allowed to complete within graceful_shutdown_timeout
69
+ 4. Worker exits early if all activities complete, or at timeout
70
+
56
71
  Note:
57
72
  This class is designed to be thread-safe when running workers in daemon mode.
58
73
  However, care should be taken when modifying worker attributes after initialization.
@@ -71,6 +86,78 @@ class Worker:
71
86
 
72
87
  default_passthrough_modules = ["application_sdk", "pandas", "os", "app"]
73
88
 
89
+ def _setup_signal_handlers(self) -> None:
90
+ """Set up SIGTERM and SIGINT handlers for graceful shutdown.
91
+
92
+ Signal handlers can only be registered from the main thread.
93
+
94
+ Platform Notes:
95
+ - Unix/Linux/macOS: Full signal handling support via asyncio event loop.
96
+ - Windows: Signal handling is not supported. Workers on Windows will not
97
+ respond to SIGTERM/SIGINT for graceful shutdown. On Windows, the worker
98
+ will continue running until the process is forcefully terminated.
99
+ """
100
+ # Signal handlers only work on Unix-like systems
101
+ if sys.platform in ("win32", "cygwin"):
102
+ logger.warning(
103
+ "Signal handlers not supported on Windows. "
104
+ "Graceful shutdown via SIGTERM/SIGINT is not available. "
105
+ "For production deployments, use Unix-based systems."
106
+ )
107
+ return
108
+
109
+ # Signal handlers can only be registered from the main thread
110
+ if threading.current_thread() is not threading.main_thread():
111
+ logger.debug(
112
+ "Skipping signal handler registration - not running in main thread"
113
+ )
114
+ return
115
+
116
+ loop = asyncio.get_running_loop()
117
+
118
+ def handle_signal(sig_name: str) -> None:
119
+ """Handle shutdown signal by triggering worker.shutdown().
120
+
121
+ Uses a flag to prevent multiple shutdown tasks from being created
122
+ if multiple signals are received in quick succession.
123
+ """
124
+ if self._shutdown_initiated:
125
+ logger.debug(f"Received {sig_name}, but shutdown already in progress")
126
+ return
127
+
128
+ self._shutdown_initiated = True
129
+ logger.info(
130
+ f"Received {sig_name}, initiating graceful shutdown "
131
+ f"(timeout: {GRACEFUL_SHUTDOWN_TIMEOUT_SECONDS}s)"
132
+ )
133
+ if self.workflow_worker:
134
+ asyncio.create_task(self._shutdown_worker())
135
+
136
+ try:
137
+ loop.add_signal_handler(signal.SIGTERM, lambda: handle_signal("SIGTERM"))
138
+ loop.add_signal_handler(signal.SIGINT, lambda: handle_signal("SIGINT"))
139
+ logger.debug("Registered SIGTERM and SIGINT handlers")
140
+ except (ValueError, RuntimeError) as e:
141
+ logger.warning(f"Could not set up signal handlers: {e}")
142
+
143
+ async def _shutdown_worker(self) -> None:
144
+ """Shutdown the worker gracefully.
145
+
146
+ Calls worker.shutdown() which:
147
+ 1. Stops polling for new tasks
148
+ 2. Waits for in-flight activities (up to graceful_shutdown_timeout)
149
+ 3. Returns when done or timeout reached
150
+ """
151
+ if not self.workflow_worker:
152
+ return
153
+
154
+ try:
155
+ logger.info("Stopping polling, waiting for in-flight activities...")
156
+ await self.workflow_worker.shutdown()
157
+ logger.info("Worker shutdown complete")
158
+ except Exception as e:
159
+ logger.error(f"Error during shutdown: {e}")
160
+
74
161
  def __init__(
75
162
  self,
76
163
  workflow_client: Optional[WorkflowClient] = None,
@@ -105,6 +192,7 @@ class Worker:
105
192
  """
106
193
  self.workflow_client = workflow_client
107
194
  self.workflow_worker: Optional[TemporalWorker] = None
195
+ self._shutdown_initiated = False
108
196
  self.workflow_activities = workflow_activities
109
197
  self.workflow_classes = workflow_classes
110
198
  self.passthrough_modules = list(
@@ -153,9 +241,11 @@ class Worker:
153
241
  RuntimeError: If worker creation fails.
154
242
  ConnectionError: If connection to Temporal server fails.
155
243
 
156
- Note:
157
- When running as a daemon, the worker runs in a separate thread and
158
- does not block the main thread.
244
+ Graceful Shutdown:
245
+ When SIGTERM/SIGINT is received:
246
+ 1. Worker stops polling for new tasks
247
+ 2. In-flight activities complete within graceful_shutdown_timeout
248
+ 3. Worker exits early if activities finish, or at timeout
159
249
  """
160
250
  if daemon:
161
251
  worker_thread = threading.Thread(
@@ -184,11 +274,19 @@ class Worker:
184
274
  max_concurrent_activities=self.max_concurrent_activities,
185
275
  activity_executor=self.activity_executor,
186
276
  )
277
+ self.workflow_worker = worker
187
278
 
188
279
  logger.info(
189
280
  f"Starting worker with task queue: {self.workflow_client.worker_task_queue}"
190
281
  )
282
+
283
+ # Set up signal handlers and run worker
284
+ self._setup_signal_handlers()
285
+
191
286
  await worker.run()
287
+
288
+ logger.info("Worker stopped")
289
+
192
290
  except Exception as e:
193
291
  logger.error(f"Error starting worker: {e}")
194
292
  raise e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlan-application-sdk
3
- Version: 2.1.1
3
+ Version: 2.3.0
4
4
  Summary: Atlan Application SDK is a Python library for developing applications on the Atlan Platform
5
5
  Project-URL: Repository, https://github.com/atlanhq/application-sdk
6
6
  Project-URL: Documentation, https://github.com/atlanhq/application-sdk/README.md
@@ -30,6 +30,10 @@ Requires-Dist: pyatlan<8.5.0,>=8.0.2
30
30
  Requires-Dist: pydantic<2.13.0,>=2.10.6
31
31
  Requires-Dist: python-dotenv<1.3.0,>=1.1.0
32
32
  Requires-Dist: uvloop<0.23.0,>=0.21.0; sys_platform != 'win32'
33
+ Provides-Extra: azure
34
+ Requires-Dist: azure-identity>=1.15.0; extra == 'azure'
35
+ Requires-Dist: azure-storage-blob>=12.19.0; extra == 'azure'
36
+ Requires-Dist: azure-storage-file-datalake>=12.19.0; extra == 'azure'
33
37
  Provides-Extra: daft
34
38
  Requires-Dist: daft<0.8.0,>=0.7.1; extra == 'daft'
35
39
  Provides-Extra: distributed-lock
@@ -1,7 +1,7 @@
1
1
  application_sdk/__init__.py,sha256=2e2mvmLJ5dxmJGPELtb33xwP-j6JMdoIuqKycEn7hjg,151
2
- application_sdk/constants.py,sha256=TvdmKQShVWBNQZdVF2y-fxuE31FmeraTnqQ9jT_n5XY,11567
3
- application_sdk/version.py,sha256=sNbvXviG7NgxM58lOHKhbZfERat5qAJNr3UZy_toVQs,84
4
- application_sdk/worker.py,sha256=DLMocpHvvwpdAopyXhxwM7ftaNlKvZMQfkgy1MFyiik,7561
2
+ application_sdk/constants.py,sha256=_JprQls6AurPBHMG8TgGkLT7q4Lj8YxMAabYQx8Heks,12544
3
+ application_sdk/version.py,sha256=Lnq43x1REzO0vMqhztEDG59AS0CKBDL70U5_-QOtwwc,84
4
+ application_sdk/worker.py,sha256=5V_zxkx64moHSkMaZTNnbfstoygMBk7SAOSEy6AZZZM,11552
5
5
  application_sdk/activities/__init__.py,sha256=i7iY6aL1VFg185n2rLLvD_sI2BA9zJ33jL5rD_sY__U,12350
6
6
  application_sdk/activities/lock_management.py,sha256=6Wdf3jMKitoarHQP91PIJOoGFz4aaOLS_40c7n1yAOA,3902
7
7
  application_sdk/activities/.cursor/BUGBOT.md,sha256=FNykX5aMkdOhzgpiGqstOnSp9JN63iR2XP3onU4AGh8,15843
@@ -15,8 +15,8 @@ application_sdk/activities/metadata_extraction/rest.py,sha256=47DEQpj8HBSa-_TImW
15
15
  application_sdk/activities/metadata_extraction/sql.py,sha256=CmE77EsgbOuDL5AKaRCnq1jApJnDWNVxx-RZ49cJwus,27415
16
16
  application_sdk/activities/query_extraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  application_sdk/activities/query_extraction/sql.py,sha256=Gsa79R8CYY0uyt3rA2nLMfQs8-C4_zg1pJ_yYSF2cZw,21193
18
- application_sdk/application/__init__.py,sha256=vcrQsqlfmGvKcCZuOtHHaNRqHSGdXlEDftkb8Tv_shI,9867
19
- application_sdk/application/metadata_extraction/sql.py,sha256=rOd06Wodr4GyzupCYxVSCsNcuNar1rJM66ej9vocNHw,8138
18
+ application_sdk/application/__init__.py,sha256=sTgyt4MBDvsQJ2yI0TxzB1Wp68WC-5fBkSExUgtW8LY,12559
19
+ application_sdk/application/metadata_extraction/sql.py,sha256=TmHTLdWLxqtSbVyzfNmnQNe6BUePVIlZxaiQHZXXbyI,9812
20
20
  application_sdk/clients/__init__.py,sha256=C9T84J7V6ZumcoWJPAxdd3tqSmbyciaGBJn-CaCCny0,1341
21
21
  application_sdk/clients/atlan.py,sha256=l6yV39fr1006SJFwkOTNDQlbSFlHCZQaUPfdUlzdVEg,5053
22
22
  application_sdk/clients/atlan_auth.py,sha256=_MykgutI-Ill1t8ERgc1a7QrfaxnrtZjD48FAT-ER9M,8642
@@ -24,13 +24,16 @@ application_sdk/clients/base.py,sha256=TIn3pG89eXUc1XSYf4jk66m1vajWp0WxcCQOOltda
24
24
  application_sdk/clients/models.py,sha256=iZOTyH6LO64kozdiUPCFCN0NgLhd_Gtv0lH7ZIPdo8w,1800
25
25
  application_sdk/clients/redis.py,sha256=IfAD32vLp88BCvsDTaQtxFHxzHlEx4V7TK7h1HwDDBg,15917
26
26
  application_sdk/clients/sql.py,sha256=J43FCxLW2YbnH2MlSm5hCTRFOMOEBtHFqi4ZTTul4JQ,26300
27
- application_sdk/clients/temporal.py,sha256=7ZkQSwSSZTFkBkhwFlqmLBFxlD2-jLS8QWpSiFTX7V4,20024
27
+ application_sdk/clients/temporal.py,sha256=7xxtzHrZdWjZ5pY1UPoRDY8U9IGpaGycANoWLY-r3io,20238
28
28
  application_sdk/clients/utils.py,sha256=zLFOJbTr_6TOqnjfVFGY85OtIXZ4FQy_rquzjaydkbY,779
29
29
  application_sdk/clients/workflow.py,sha256=6bSqmA3sNCk9oY68dOjBUDZ9DhNKQxPD75qqE0cfldc,6104
30
30
  application_sdk/clients/.cursor/BUGBOT.md,sha256=7nEDUqWBEMI_uU6eK1jCSZGeXoQtLQcKwOrDn8AIDWo,10595
31
+ application_sdk/clients/azure/__init__.py,sha256=koHM4dpsLDf6NQfBWh1axIwF8DGFePcc_nu8_4a-D1g,233
32
+ application_sdk/clients/azure/auth.py,sha256=lvdyNt0V099XlSuTapprNx9xmtpr1El5I_2pKtEnDX0,11129
33
+ application_sdk/clients/azure/client.py,sha256=7f2kanMWgF61xM3WXeFnsgrZaG3Pdl9a1Vt8818e8HQ,13538
31
34
  application_sdk/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
35
  application_sdk/common/aws_utils.py,sha256=xlSMIQyjvQ-CydEXaxXrnPUygv7AAbCLsxhZ2wtKnzg,11219
33
- application_sdk/common/error_codes.py,sha256=bxgvugN_0H5b8VXfJw-44mybgX5I9lRJbRdYjtPjqDI,14561
36
+ application_sdk/common/error_codes.py,sha256=BGdOSfhKe4pd9oM9T2N8GASZeIJU2Be1NcThL8LkKBQ,14900
34
37
  application_sdk/common/file_converter.py,sha256=ta0PVh7uIEGJg0BTPUJnSjj55ve2iVAOkqwAeg96_-g,3079
35
38
  application_sdk/common/types.py,sha256=qkVr3SAR1zn5_0w9hFt18vGtcnaPLKlMJLSBhTSKePU,134
36
39
  application_sdk/common/utils.py,sha256=czcWvqoe2PawDvHOahk_AI88Zqth-CM3KzdGmehHQJ4,19286
@@ -78,8 +81,8 @@ application_sdk/observability/utils.py,sha256=-02GAFom8Bg4SNyCTNYySmen2dzvLfTu43
78
81
  application_sdk/observability/decorators/observability_decorator.py,sha256=yd6qfrg1MmH5KcZ5Ydzb0RaBzmxx5FrmiI9qwvZx3EU,8963
79
82
  application_sdk/server/__init__.py,sha256=KTqE1YPw_3WDVMWatJUuf9OOiobLM2K5SMaBrI62sCo,1568
80
83
  application_sdk/server/.cursor/BUGBOT.md,sha256=p_MMoWUW5G1894WfOKYReZKWCuyJT_OJz3rL5g21NbI,16566
81
- application_sdk/server/fastapi/__init__.py,sha256=BVqf63z1hxEdpJqLU4LXpFTbk5q8dVkjEbWbu_vbW_Y,29578
82
- application_sdk/server/fastapi/models.py,sha256=h0hMtMg_p5G0Ug2MBkmBcT94W025VKCLMFyh0FciNoQ,7559
84
+ application_sdk/server/fastapi/__init__.py,sha256=Ce1eXW9Y_8icuEL3RawYfoqcJbjpyfEK2nHq9y-o04k,30941
85
+ application_sdk/server/fastapi/models.py,sha256=ZKz0RWISrJKQ50kepPiPsVqZ6li44I_hFIy5GXochLc,9796
83
86
  application_sdk/server/fastapi/utils.py,sha256=WoDGDmq6E1kwS2FN5pjIuzygKNTHpA-tg2SQJZgJOOI,1415
84
87
  application_sdk/server/fastapi/middleware/logmiddleware.py,sha256=sKKi-ysI9XQDT0uKW3sfw2r3XQq2iJT4_XrNGXZWdjI,2907
85
88
  application_sdk/server/fastapi/middleware/metrics.py,sha256=F_EhayzNgEn9KJvITv8VFxwXhBZVaOQkDptyfa9nQK4,1820
@@ -138,7 +141,7 @@ application_sdk/transformers/atlas/__init__.py,sha256=fw3D8bBtt61SseAfYut3JZddpX
138
141
  application_sdk/transformers/atlas/sql.py,sha256=rkQXNZ7oebts5oF5E_Bw8NpcHHKScU0TmKciH_1l_k4,50419
139
142
  application_sdk/transformers/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
143
  application_sdk/transformers/common/utils.py,sha256=4ISMIQ0Gzghmi31p51FOFm5KLF7XF-fmH9PVT7i0DFE,4899
141
- application_sdk/transformers/query/__init__.py,sha256=4uVCU-NfDe08PlffjWQ5p4smQa7c518IL2rDgIk6694,17446
144
+ application_sdk/transformers/query/__init__.py,sha256=asp3IC0zLjrwM8wyUxI8FSFeXmBT5IoFZ23IHn67Muw,18711
142
145
  application_sdk/transformers/query/templates/column.yaml,sha256=EXLYwGXN7LKT-v51n2EZnY99o6vHucyFaVSpM-sUSXw,7679
143
146
  application_sdk/transformers/query/templates/database.yaml,sha256=SD1hJg5LI7gsBHQL5mW341sa51EkhcsIDDFlIOi9zdk,1374
144
147
  application_sdk/transformers/query/templates/extras-procedure.yaml,sha256=XhAfVY4zm99K8fcgkYA1XPLv4ks-SA6SzMO3SMtQ60s,2298
@@ -152,8 +155,8 @@ application_sdk/workflows/metadata_extraction/__init__.py,sha256=jHUe_ZBQ66jx8bg
152
155
  application_sdk/workflows/metadata_extraction/sql.py,sha256=6ZaVt84n-8U2ZvR9GR7uIJKv5v8CuyQjhlnoRJvDszc,12435
153
156
  application_sdk/workflows/query_extraction/__init__.py,sha256=n066_CX5RpJz6DIxGMkKS3eGSRg03ilaCtsqfJWQb7Q,117
154
157
  application_sdk/workflows/query_extraction/sql.py,sha256=kT_JQkLCRZ44ZpaC4QvPL6DxnRIIVh8gYHLqRbMI-hA,4826
155
- atlan_application_sdk-2.1.1.dist-info/METADATA,sha256=Vc2uG2FMhuNXyZFXmGMmvc_LRpCBaNTcQEHpSV8NpOE,5805
156
- atlan_application_sdk-2.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
157
- atlan_application_sdk-2.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
158
- atlan_application_sdk-2.1.1.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
159
- atlan_application_sdk-2.1.1.dist-info/RECORD,,
158
+ atlan_application_sdk-2.3.0.dist-info/METADATA,sha256=F-6xR-DchbERcVzifQhz_M4jjUhcYke0tZhe3vIwHao,6014
159
+ atlan_application_sdk-2.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
160
+ atlan_application_sdk-2.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
161
+ atlan_application_sdk-2.3.0.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
162
+ atlan_application_sdk-2.3.0.dist-info/RECORD,,