mcp-proxy-adapter 6.6.9__py3-none-any.whl → 6.7.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.
@@ -0,0 +1,170 @@
1
+ """
2
+ Signal handler for graceful shutdown with proxy unregistration.
3
+
4
+ This module provides signal handling for SIGTERM, SIGINT, and SIGHUP
5
+ to ensure proper proxy unregistration before server shutdown.
6
+
7
+ Author: Vasiliy Zdanovskiy
8
+ email: vasilyvz@gmail.com
9
+ """
10
+
11
+ import signal
12
+ import asyncio
13
+ import threading
14
+ from typing import Optional, Callable, Any
15
+ from mcp_proxy_adapter.core.logging import logger
16
+
17
+
18
+ class SignalHandler:
19
+ """
20
+ Signal handler for graceful shutdown with proxy unregistration.
21
+ """
22
+
23
+ def __init__(self):
24
+ """Initialize signal handler."""
25
+ self._shutdown_callback: Optional[Callable] = None
26
+ self._shutdown_event = threading.Event()
27
+ self._original_handlers = {}
28
+ self._setup_signal_handlers()
29
+
30
+ def set_shutdown_callback(self, callback: Callable):
31
+ """
32
+ Set callback function to be called during shutdown.
33
+
34
+ Args:
35
+ callback: Function to call during shutdown
36
+ """
37
+ self._shutdown_callback = callback
38
+ logger.info("Shutdown callback set for signal handler")
39
+
40
+ def _setup_signal_handlers(self):
41
+ """Setup signal handlers for graceful shutdown."""
42
+ # Handle SIGTERM (termination signal)
43
+ self._original_handlers[signal.SIGTERM] = signal.signal(
44
+ signal.SIGTERM, self._handle_shutdown_signal
45
+ )
46
+
47
+ # Handle SIGINT (Ctrl+C)
48
+ self._original_handlers[signal.SIGINT] = signal.signal(
49
+ signal.SIGINT, self._handle_shutdown_signal
50
+ )
51
+
52
+ # Handle SIGHUP (hangup signal)
53
+ self._original_handlers[signal.SIGHUP] = signal.signal(
54
+ signal.SIGHUP, self._handle_shutdown_signal
55
+ )
56
+
57
+ logger.info("Signal handlers installed for SIGTERM, SIGINT, SIGHUP")
58
+
59
+ def _handle_shutdown_signal(self, signum: int, frame: Any):
60
+ """
61
+ Handle shutdown signals.
62
+
63
+ Args:
64
+ signum: Signal number
65
+ frame: Current stack frame
66
+ """
67
+ signal_name = signal.Signals(signum).name
68
+ logger.info(f"🛑 Received {signal_name} signal, initiating graceful shutdown...")
69
+
70
+ # Set shutdown event
71
+ self._shutdown_event.set()
72
+
73
+ # Call shutdown callback if set
74
+ if self._shutdown_callback:
75
+ try:
76
+ logger.info("🔄 Executing shutdown callback...")
77
+ self._shutdown_callback()
78
+ logger.info("✅ Shutdown callback completed successfully")
79
+ except Exception as e:
80
+ logger.error(f"❌ Shutdown callback failed: {e}")
81
+
82
+ # If this is SIGINT (Ctrl+C), we might want to exit immediately after a delay
83
+ if signum == signal.SIGINT:
84
+ logger.info("⏰ SIGINT received, will exit in 5 seconds if not stopped gracefully...")
85
+ threading.Timer(5.0, self._force_exit).start()
86
+
87
+ def _force_exit(self):
88
+ """Force exit if graceful shutdown takes too long."""
89
+ if self._shutdown_event.is_set():
90
+ logger.warning("⚠️ Forcing exit after timeout")
91
+ import os
92
+ os._exit(1)
93
+
94
+ def wait_for_shutdown(self, timeout: Optional[float] = None) -> bool:
95
+ """
96
+ Wait for shutdown signal.
97
+
98
+ Args:
99
+ timeout: Maximum time to wait in seconds
100
+
101
+ Returns:
102
+ True if shutdown signal received, False if timeout
103
+ """
104
+ return self._shutdown_event.wait(timeout)
105
+
106
+ def is_shutdown_requested(self) -> bool:
107
+ """
108
+ Check if shutdown has been requested.
109
+
110
+ Returns:
111
+ True if shutdown signal received
112
+ """
113
+ return self._shutdown_event.is_set()
114
+
115
+ def restore_handlers(self):
116
+ """Restore original signal handlers."""
117
+ for sig, handler in self._original_handlers.items():
118
+ if handler is not None:
119
+ signal.signal(sig, handler)
120
+ logger.info("Original signal handlers restored")
121
+
122
+
123
+ # Global signal handler instance
124
+ _signal_handler: Optional[SignalHandler] = None
125
+
126
+
127
+ def get_signal_handler() -> SignalHandler:
128
+ """Get the global signal handler instance."""
129
+ global _signal_handler
130
+ if _signal_handler is None:
131
+ _signal_handler = SignalHandler()
132
+ return _signal_handler
133
+
134
+
135
+ def setup_signal_handling(shutdown_callback: Optional[Callable] = None):
136
+ """
137
+ Setup signal handling for graceful shutdown.
138
+
139
+ Args:
140
+ shutdown_callback: Optional callback to execute during shutdown
141
+ """
142
+ handler = get_signal_handler()
143
+ if shutdown_callback:
144
+ handler.set_shutdown_callback(shutdown_callback)
145
+ logger.info("Signal handling setup completed")
146
+
147
+
148
+ def wait_for_shutdown_signal(timeout: Optional[float] = None) -> bool:
149
+ """
150
+ Wait for shutdown signal.
151
+
152
+ Args:
153
+ timeout: Maximum time to wait in seconds
154
+
155
+ Returns:
156
+ True if shutdown signal received, False if timeout
157
+ """
158
+ handler = get_signal_handler()
159
+ return handler.wait_for_shutdown(timeout)
160
+
161
+
162
+ def is_shutdown_requested() -> bool:
163
+ """
164
+ Check if shutdown has been requested.
165
+
166
+ Returns:
167
+ True if shutdown signal received
168
+ """
169
+ handler = get_signal_handler()
170
+ return handler.is_shutdown_requested()