kstlib 0.0.1a0__py3-none-any.whl → 1.0.1__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.
Files changed (166) hide show
  1. kstlib/__init__.py +266 -1
  2. kstlib/__main__.py +16 -0
  3. kstlib/alerts/__init__.py +110 -0
  4. kstlib/alerts/channels/__init__.py +36 -0
  5. kstlib/alerts/channels/base.py +197 -0
  6. kstlib/alerts/channels/email.py +227 -0
  7. kstlib/alerts/channels/slack.py +389 -0
  8. kstlib/alerts/exceptions.py +72 -0
  9. kstlib/alerts/manager.py +651 -0
  10. kstlib/alerts/models.py +142 -0
  11. kstlib/alerts/throttle.py +263 -0
  12. kstlib/auth/__init__.py +139 -0
  13. kstlib/auth/callback.py +399 -0
  14. kstlib/auth/config.py +502 -0
  15. kstlib/auth/errors.py +127 -0
  16. kstlib/auth/models.py +316 -0
  17. kstlib/auth/providers/__init__.py +14 -0
  18. kstlib/auth/providers/base.py +393 -0
  19. kstlib/auth/providers/oauth2.py +645 -0
  20. kstlib/auth/providers/oidc.py +821 -0
  21. kstlib/auth/session.py +338 -0
  22. kstlib/auth/token.py +482 -0
  23. kstlib/cache/__init__.py +50 -0
  24. kstlib/cache/decorator.py +261 -0
  25. kstlib/cache/strategies.py +516 -0
  26. kstlib/cli/__init__.py +8 -0
  27. kstlib/cli/app.py +195 -0
  28. kstlib/cli/commands/__init__.py +5 -0
  29. kstlib/cli/commands/auth/__init__.py +39 -0
  30. kstlib/cli/commands/auth/common.py +122 -0
  31. kstlib/cli/commands/auth/login.py +325 -0
  32. kstlib/cli/commands/auth/logout.py +74 -0
  33. kstlib/cli/commands/auth/providers.py +57 -0
  34. kstlib/cli/commands/auth/status.py +291 -0
  35. kstlib/cli/commands/auth/token.py +199 -0
  36. kstlib/cli/commands/auth/whoami.py +106 -0
  37. kstlib/cli/commands/config.py +89 -0
  38. kstlib/cli/commands/ops/__init__.py +39 -0
  39. kstlib/cli/commands/ops/attach.py +49 -0
  40. kstlib/cli/commands/ops/common.py +269 -0
  41. kstlib/cli/commands/ops/list_sessions.py +252 -0
  42. kstlib/cli/commands/ops/logs.py +49 -0
  43. kstlib/cli/commands/ops/start.py +98 -0
  44. kstlib/cli/commands/ops/status.py +138 -0
  45. kstlib/cli/commands/ops/stop.py +60 -0
  46. kstlib/cli/commands/rapi/__init__.py +60 -0
  47. kstlib/cli/commands/rapi/call.py +341 -0
  48. kstlib/cli/commands/rapi/list.py +99 -0
  49. kstlib/cli/commands/rapi/show.py +206 -0
  50. kstlib/cli/commands/secrets/__init__.py +35 -0
  51. kstlib/cli/commands/secrets/common.py +425 -0
  52. kstlib/cli/commands/secrets/decrypt.py +88 -0
  53. kstlib/cli/commands/secrets/doctor.py +743 -0
  54. kstlib/cli/commands/secrets/encrypt.py +242 -0
  55. kstlib/cli/commands/secrets/shred.py +96 -0
  56. kstlib/cli/common.py +86 -0
  57. kstlib/config/__init__.py +76 -0
  58. kstlib/config/exceptions.py +110 -0
  59. kstlib/config/export.py +225 -0
  60. kstlib/config/loader.py +963 -0
  61. kstlib/config/sops.py +287 -0
  62. kstlib/db/__init__.py +54 -0
  63. kstlib/db/aiosqlcipher.py +137 -0
  64. kstlib/db/cipher.py +112 -0
  65. kstlib/db/database.py +367 -0
  66. kstlib/db/exceptions.py +25 -0
  67. kstlib/db/pool.py +302 -0
  68. kstlib/helpers/__init__.py +35 -0
  69. kstlib/helpers/exceptions.py +11 -0
  70. kstlib/helpers/time_trigger.py +396 -0
  71. kstlib/kstlib.conf.yml +890 -0
  72. kstlib/limits.py +963 -0
  73. kstlib/logging/__init__.py +108 -0
  74. kstlib/logging/manager.py +633 -0
  75. kstlib/mail/__init__.py +42 -0
  76. kstlib/mail/builder.py +626 -0
  77. kstlib/mail/exceptions.py +27 -0
  78. kstlib/mail/filesystem.py +248 -0
  79. kstlib/mail/transport.py +224 -0
  80. kstlib/mail/transports/__init__.py +19 -0
  81. kstlib/mail/transports/gmail.py +268 -0
  82. kstlib/mail/transports/resend.py +324 -0
  83. kstlib/mail/transports/smtp.py +326 -0
  84. kstlib/meta.py +72 -0
  85. kstlib/metrics/__init__.py +88 -0
  86. kstlib/metrics/decorators.py +1090 -0
  87. kstlib/metrics/exceptions.py +14 -0
  88. kstlib/monitoring/__init__.py +116 -0
  89. kstlib/monitoring/_styles.py +163 -0
  90. kstlib/monitoring/cell.py +57 -0
  91. kstlib/monitoring/config.py +424 -0
  92. kstlib/monitoring/delivery.py +579 -0
  93. kstlib/monitoring/exceptions.py +63 -0
  94. kstlib/monitoring/image.py +220 -0
  95. kstlib/monitoring/kv.py +79 -0
  96. kstlib/monitoring/list.py +69 -0
  97. kstlib/monitoring/metric.py +88 -0
  98. kstlib/monitoring/monitoring.py +341 -0
  99. kstlib/monitoring/renderer.py +139 -0
  100. kstlib/monitoring/service.py +392 -0
  101. kstlib/monitoring/table.py +129 -0
  102. kstlib/monitoring/types.py +56 -0
  103. kstlib/ops/__init__.py +86 -0
  104. kstlib/ops/base.py +148 -0
  105. kstlib/ops/container.py +577 -0
  106. kstlib/ops/exceptions.py +209 -0
  107. kstlib/ops/manager.py +407 -0
  108. kstlib/ops/models.py +176 -0
  109. kstlib/ops/tmux.py +372 -0
  110. kstlib/ops/validators.py +287 -0
  111. kstlib/py.typed +0 -0
  112. kstlib/rapi/__init__.py +118 -0
  113. kstlib/rapi/client.py +875 -0
  114. kstlib/rapi/config.py +861 -0
  115. kstlib/rapi/credentials.py +887 -0
  116. kstlib/rapi/exceptions.py +213 -0
  117. kstlib/resilience/__init__.py +101 -0
  118. kstlib/resilience/circuit_breaker.py +440 -0
  119. kstlib/resilience/exceptions.py +95 -0
  120. kstlib/resilience/heartbeat.py +491 -0
  121. kstlib/resilience/rate_limiter.py +506 -0
  122. kstlib/resilience/shutdown.py +417 -0
  123. kstlib/resilience/watchdog.py +637 -0
  124. kstlib/secrets/__init__.py +29 -0
  125. kstlib/secrets/exceptions.py +19 -0
  126. kstlib/secrets/models.py +62 -0
  127. kstlib/secrets/providers/__init__.py +79 -0
  128. kstlib/secrets/providers/base.py +58 -0
  129. kstlib/secrets/providers/environment.py +66 -0
  130. kstlib/secrets/providers/keyring.py +107 -0
  131. kstlib/secrets/providers/kms.py +223 -0
  132. kstlib/secrets/providers/kwargs.py +101 -0
  133. kstlib/secrets/providers/sops.py +209 -0
  134. kstlib/secrets/resolver.py +221 -0
  135. kstlib/secrets/sensitive.py +130 -0
  136. kstlib/secure/__init__.py +23 -0
  137. kstlib/secure/fs.py +194 -0
  138. kstlib/secure/permissions.py +70 -0
  139. kstlib/ssl.py +347 -0
  140. kstlib/ui/__init__.py +23 -0
  141. kstlib/ui/exceptions.py +26 -0
  142. kstlib/ui/panels.py +484 -0
  143. kstlib/ui/spinner.py +864 -0
  144. kstlib/ui/tables.py +382 -0
  145. kstlib/utils/__init__.py +48 -0
  146. kstlib/utils/dict.py +36 -0
  147. kstlib/utils/formatting.py +338 -0
  148. kstlib/utils/http_trace.py +237 -0
  149. kstlib/utils/lazy.py +49 -0
  150. kstlib/utils/secure_delete.py +205 -0
  151. kstlib/utils/serialization.py +247 -0
  152. kstlib/utils/text.py +56 -0
  153. kstlib/utils/validators.py +124 -0
  154. kstlib/websocket/__init__.py +97 -0
  155. kstlib/websocket/exceptions.py +214 -0
  156. kstlib/websocket/manager.py +1102 -0
  157. kstlib/websocket/models.py +361 -0
  158. kstlib-1.0.1.dist-info/METADATA +201 -0
  159. kstlib-1.0.1.dist-info/RECORD +163 -0
  160. {kstlib-0.0.1a0.dist-info → kstlib-1.0.1.dist-info}/WHEEL +1 -1
  161. kstlib-1.0.1.dist-info/entry_points.txt +2 -0
  162. kstlib-1.0.1.dist-info/licenses/LICENSE.md +9 -0
  163. kstlib-0.0.1a0.dist-info/METADATA +0 -29
  164. kstlib-0.0.1a0.dist-info/RECORD +0 -6
  165. kstlib-0.0.1a0.dist-info/licenses/LICENSE.md +0 -5
  166. {kstlib-0.0.1a0.dist-info → kstlib-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,97 @@
1
+ """WebSocket client with proactive connection control.
2
+
3
+ This module provides WebSocketManager, an async WebSocket client that offers
4
+ proactive control over connections rather than just reactive reconnection.
5
+
6
+ The key differentiator is the ability to control WHEN to disconnect/reconnect
7
+ rather than just reacting to disconnections. This is essential for trading
8
+ applications where you want to avoid disconnections during critical operations.
9
+
10
+ Features:
11
+ - **Proactive Control**: User-controlled disconnect/reconnect timing
12
+ - **Auto-Reconnection**: Configurable reconnection strategies
13
+ - **Subscription Management**: Auto-resubscribe on reconnection
14
+ - **Statistics Tracking**: Proactive vs reactive disconnect metrics
15
+ - **Config-Driven**: Integrates with kstlib.conf.yml
16
+
17
+ Examples:
18
+ Basic usage:
19
+
20
+ >>> from kstlib.websocket import WebSocketManager
21
+ >>> async def main(): # doctest: +SKIP
22
+ ... async with WebSocketManager("wss://example.com/ws") as ws:
23
+ ... async for message in ws.stream():
24
+ ... print(message)
25
+
26
+ Proactive control for trading:
27
+
28
+ >>> def next_candle_in() -> float: # doctest: +SKIP
29
+ ... '''Seconds until next 4H candle.'''
30
+ ... ...
31
+ >>> async def trading(): # doctest: +SKIP
32
+ ... ws = WebSocketManager(
33
+ ... url="wss://stream.binance.com/ws/btcusdt@kline_4h",
34
+ ... # Disconnect when > 30s until next candle
35
+ ... should_disconnect=lambda: next_candle_in() > 30,
36
+ ... # Reconnect when < 60s until next candle
37
+ ... should_reconnect=lambda: next_candle_in() < 60,
38
+ ... disconnect_check_interval=5.0,
39
+ ... )
40
+ ... async with ws:
41
+ ... async for candle in ws.stream():
42
+ ... if candle.get("k", {}).get("x"):
43
+ ... process_candle(candle)
44
+
45
+ Note:
46
+ Requires the ``websockets`` package. Install with::
47
+
48
+ pip install kstlib[websocket]
49
+ """
50
+
51
+ from kstlib.websocket.exceptions import (
52
+ WebSocketClosedError,
53
+ WebSocketConnectionError,
54
+ WebSocketError,
55
+ WebSocketProtocolError,
56
+ WebSocketQueueFullError,
57
+ WebSocketReconnectError,
58
+ WebSocketTimeoutError,
59
+ )
60
+ from kstlib.websocket.models import (
61
+ ConnectionState,
62
+ DisconnectReason,
63
+ ReconnectStrategy,
64
+ WebSocketStats,
65
+ )
66
+
67
+ # Lazy import for WebSocketManager to avoid ImportError if websockets not installed
68
+ _websocket_manager = None
69
+
70
+
71
+ def __getattr__(name: str) -> type:
72
+ """Lazy load WebSocketManager to avoid ImportError if websockets not installed."""
73
+ if name == "WebSocketManager":
74
+ global _websocket_manager
75
+ if _websocket_manager is None:
76
+ from kstlib.websocket.manager import WebSocketManager
77
+
78
+ _websocket_manager = WebSocketManager
79
+ return _websocket_manager
80
+ msg = f"module {__name__!r} has no attribute {name!r}"
81
+ raise AttributeError(msg)
82
+
83
+
84
+ __all__ = [
85
+ "ConnectionState",
86
+ "DisconnectReason",
87
+ "ReconnectStrategy",
88
+ "WebSocketClosedError",
89
+ "WebSocketConnectionError",
90
+ "WebSocketError",
91
+ "WebSocketManager",
92
+ "WebSocketProtocolError",
93
+ "WebSocketQueueFullError",
94
+ "WebSocketReconnectError",
95
+ "WebSocketStats",
96
+ "WebSocketTimeoutError",
97
+ ]
@@ -0,0 +1,214 @@
1
+ """WebSocket exception hierarchy.
2
+
3
+ All WebSocket exceptions inherit from WebSocketError, allowing users to catch
4
+ all WebSocket-specific errors with a single except clause.
5
+
6
+ Example:
7
+ >>> from kstlib.websocket import WebSocketError
8
+ >>> try:
9
+ ... await manager.connect()
10
+ ... except WebSocketError as e: # doctest: +SKIP
11
+ ... print(f"WebSocket error: {e}")
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from kstlib.config.exceptions import KstlibError
17
+
18
+ __all__ = [
19
+ "WebSocketClosedError",
20
+ "WebSocketConnectionError",
21
+ "WebSocketError",
22
+ "WebSocketProtocolError",
23
+ "WebSocketQueueFullError",
24
+ "WebSocketReconnectError",
25
+ "WebSocketTimeoutError",
26
+ ]
27
+
28
+
29
+ class WebSocketError(KstlibError):
30
+ """Base exception for all WebSocket errors.
31
+
32
+ All WebSocket-specific exceptions inherit from this class,
33
+ allowing for easy catching of any WebSocket error.
34
+ """
35
+
36
+
37
+ class WebSocketConnectionError(WebSocketError, ConnectionError):
38
+ """Failed to establish WebSocket connection.
39
+
40
+ Raised when the initial connection or reconnection fails after
41
+ exhausting all retry attempts.
42
+
43
+ Attributes:
44
+ url: The WebSocket URL that failed to connect.
45
+ attempts: Number of connection attempts made.
46
+ last_error: The underlying error from the last attempt.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ message: str,
52
+ *,
53
+ url: str = "",
54
+ attempts: int = 0,
55
+ last_error: BaseException | None = None,
56
+ ) -> None:
57
+ """Initialize connection error.
58
+
59
+ Args:
60
+ message: Human-readable error description.
61
+ url: The WebSocket URL that failed to connect.
62
+ attempts: Number of connection attempts made.
63
+ last_error: The underlying error from the last attempt.
64
+ """
65
+ super().__init__(message)
66
+ self.url = url
67
+ self.attempts = attempts
68
+ self.last_error = last_error
69
+
70
+
71
+ class WebSocketClosedError(WebSocketError):
72
+ """WebSocket connection was closed.
73
+
74
+ Raised when the connection is closed unexpectedly by the server
75
+ or due to a protocol error.
76
+
77
+ Attributes:
78
+ code: WebSocket close code (1000-4999).
79
+ reason: Optional human-readable close reason.
80
+ """
81
+
82
+ def __init__(
83
+ self,
84
+ message: str,
85
+ *,
86
+ code: int = 1006,
87
+ reason: str = "",
88
+ ) -> None:
89
+ """Initialize closed error.
90
+
91
+ Args:
92
+ message: Human-readable error description.
93
+ code: WebSocket close code (1000-4999).
94
+ reason: Optional human-readable close reason.
95
+ """
96
+ super().__init__(message)
97
+ self.code = code
98
+ self.reason = reason
99
+
100
+
101
+ class WebSocketTimeoutError(WebSocketError, TimeoutError):
102
+ """WebSocket operation timed out.
103
+
104
+ Raised when a WebSocket operation (connect, ping, receive) exceeds
105
+ its configured timeout.
106
+
107
+ Attributes:
108
+ operation: The operation that timed out (connect, ping, receive).
109
+ timeout: The timeout value in seconds.
110
+ """
111
+
112
+ def __init__(
113
+ self,
114
+ message: str,
115
+ *,
116
+ operation: str = "",
117
+ timeout: float = 0.0,
118
+ ) -> None:
119
+ """Initialize timeout error.
120
+
121
+ Args:
122
+ message: Human-readable error description.
123
+ operation: The operation that timed out.
124
+ timeout: The timeout value in seconds.
125
+ """
126
+ super().__init__(message)
127
+ self.operation = operation
128
+ self.timeout = timeout
129
+
130
+
131
+ class WebSocketReconnectError(WebSocketError):
132
+ """Failed to reconnect after disconnection.
133
+
134
+ Raised when all reconnection attempts have been exhausted
135
+ without successfully re-establishing the connection.
136
+
137
+ Attributes:
138
+ attempts: Total number of reconnection attempts made.
139
+ last_error: The underlying error from the last attempt.
140
+ """
141
+
142
+ def __init__(
143
+ self,
144
+ message: str,
145
+ *,
146
+ attempts: int = 0,
147
+ last_error: BaseException | None = None,
148
+ ) -> None:
149
+ """Initialize reconnect error.
150
+
151
+ Args:
152
+ message: Human-readable error description.
153
+ attempts: Total number of reconnection attempts made.
154
+ last_error: The underlying error from the last attempt.
155
+ """
156
+ super().__init__(message)
157
+ self.attempts = attempts
158
+ self.last_error = last_error
159
+
160
+
161
+ class WebSocketProtocolError(WebSocketError):
162
+ """WebSocket protocol violation.
163
+
164
+ Raised when the server or client violates the WebSocket protocol,
165
+ such as sending malformed frames or invalid data.
166
+
167
+ Attributes:
168
+ protocol_error: Description of the protocol violation.
169
+ """
170
+
171
+ def __init__(
172
+ self,
173
+ message: str,
174
+ *,
175
+ protocol_error: str = "",
176
+ ) -> None:
177
+ """Initialize protocol error.
178
+
179
+ Args:
180
+ message: Human-readable error description.
181
+ protocol_error: Description of the protocol violation.
182
+ """
183
+ super().__init__(message)
184
+ self.protocol_error = protocol_error
185
+
186
+
187
+ class WebSocketQueueFullError(WebSocketError):
188
+ """Message queue is full.
189
+
190
+ Raised when the incoming message queue is full and cannot accept
191
+ more messages. This typically indicates the consumer is too slow.
192
+
193
+ Attributes:
194
+ queue_size: Maximum queue size that was exceeded.
195
+ dropped_count: Number of messages dropped due to queue overflow.
196
+ """
197
+
198
+ def __init__(
199
+ self,
200
+ message: str,
201
+ *,
202
+ queue_size: int = 0,
203
+ dropped_count: int = 0,
204
+ ) -> None:
205
+ """Initialize queue full error.
206
+
207
+ Args:
208
+ message: Human-readable error description.
209
+ queue_size: Maximum queue size that was exceeded.
210
+ dropped_count: Number of messages dropped due to queue overflow.
211
+ """
212
+ super().__init__(message)
213
+ self.queue_size = queue_size
214
+ self.dropped_count = dropped_count