provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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 (163) hide show
  1. provide/foundation/__init__.py +36 -10
  2. provide/foundation/archive/__init__.py +1 -1
  3. provide/foundation/archive/base.py +15 -14
  4. provide/foundation/archive/bzip2.py +40 -40
  5. provide/foundation/archive/gzip.py +42 -42
  6. provide/foundation/archive/operations.py +93 -96
  7. provide/foundation/archive/tar.py +33 -31
  8. provide/foundation/archive/zip.py +52 -50
  9. provide/foundation/asynctools/__init__.py +20 -0
  10. provide/foundation/asynctools/core.py +126 -0
  11. provide/foundation/cli/__init__.py +2 -2
  12. provide/foundation/cli/commands/deps.py +15 -9
  13. provide/foundation/cli/commands/logs/__init__.py +3 -3
  14. provide/foundation/cli/commands/logs/generate.py +2 -2
  15. provide/foundation/cli/commands/logs/query.py +4 -4
  16. provide/foundation/cli/commands/logs/send.py +3 -3
  17. provide/foundation/cli/commands/logs/tail.py +3 -3
  18. provide/foundation/cli/decorators.py +11 -11
  19. provide/foundation/cli/main.py +1 -1
  20. provide/foundation/cli/testing.py +2 -40
  21. provide/foundation/cli/utils.py +21 -18
  22. provide/foundation/config/__init__.py +35 -2
  23. provide/foundation/config/base.py +2 -2
  24. provide/foundation/config/converters.py +477 -0
  25. provide/foundation/config/defaults.py +67 -0
  26. provide/foundation/config/env.py +6 -20
  27. provide/foundation/config/loader.py +10 -4
  28. provide/foundation/config/sync.py +8 -6
  29. provide/foundation/config/types.py +5 -5
  30. provide/foundation/config/validators.py +4 -4
  31. provide/foundation/console/input.py +5 -5
  32. provide/foundation/console/output.py +36 -14
  33. provide/foundation/context/__init__.py +8 -4
  34. provide/foundation/context/core.py +88 -110
  35. provide/foundation/crypto/certificates/__init__.py +9 -5
  36. provide/foundation/crypto/certificates/base.py +2 -2
  37. provide/foundation/crypto/certificates/certificate.py +48 -19
  38. provide/foundation/crypto/certificates/factory.py +26 -18
  39. provide/foundation/crypto/certificates/generator.py +24 -23
  40. provide/foundation/crypto/certificates/loader.py +24 -16
  41. provide/foundation/crypto/certificates/operations.py +17 -10
  42. provide/foundation/crypto/certificates/trust.py +21 -21
  43. provide/foundation/env/__init__.py +28 -0
  44. provide/foundation/env/core.py +218 -0
  45. provide/foundation/errors/__init__.py +3 -3
  46. provide/foundation/errors/decorators.py +0 -234
  47. provide/foundation/errors/types.py +0 -98
  48. provide/foundation/eventsets/display.py +13 -14
  49. provide/foundation/eventsets/registry.py +61 -31
  50. provide/foundation/eventsets/resolver.py +50 -46
  51. provide/foundation/eventsets/sets/das.py +8 -8
  52. provide/foundation/eventsets/sets/database.py +14 -14
  53. provide/foundation/eventsets/sets/http.py +21 -21
  54. provide/foundation/eventsets/sets/llm.py +16 -16
  55. provide/foundation/eventsets/sets/task_queue.py +13 -13
  56. provide/foundation/eventsets/types.py +7 -7
  57. provide/foundation/file/directory.py +14 -23
  58. provide/foundation/file/lock.py +4 -3
  59. provide/foundation/hub/components.py +75 -389
  60. provide/foundation/hub/config.py +157 -0
  61. provide/foundation/hub/discovery.py +63 -0
  62. provide/foundation/hub/handlers.py +89 -0
  63. provide/foundation/hub/lifecycle.py +195 -0
  64. provide/foundation/hub/manager.py +7 -4
  65. provide/foundation/hub/processors.py +49 -0
  66. provide/foundation/integrations/__init__.py +11 -0
  67. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  68. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/client.py +14 -14
  70. provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
  71. provide/foundation/integrations/openobserve/config.py +37 -0
  72. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  73. provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
  74. provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
  75. provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
  76. provide/foundation/logger/__init__.py +0 -1
  77. provide/foundation/logger/config/base.py +1 -1
  78. provide/foundation/logger/config/logging.py +69 -299
  79. provide/foundation/logger/config/telemetry.py +39 -121
  80. provide/foundation/logger/factories.py +2 -2
  81. provide/foundation/logger/processors/main.py +12 -10
  82. provide/foundation/logger/ratelimit/limiters.py +4 -4
  83. provide/foundation/logger/ratelimit/processor.py +1 -1
  84. provide/foundation/logger/setup/coordinator.py +39 -25
  85. provide/foundation/logger/setup/processors.py +3 -3
  86. provide/foundation/logger/setup/testing.py +14 -0
  87. provide/foundation/logger/trace.py +5 -5
  88. provide/foundation/metrics/__init__.py +1 -1
  89. provide/foundation/metrics/otel.py +3 -1
  90. provide/foundation/observability/__init__.py +3 -3
  91. provide/foundation/process/__init__.py +9 -0
  92. provide/foundation/process/exit.py +48 -0
  93. provide/foundation/process/lifecycle.py +69 -46
  94. provide/foundation/resilience/__init__.py +36 -0
  95. provide/foundation/resilience/circuit.py +166 -0
  96. provide/foundation/resilience/decorators.py +236 -0
  97. provide/foundation/resilience/fallback.py +208 -0
  98. provide/foundation/resilience/retry.py +327 -0
  99. provide/foundation/serialization/__init__.py +16 -0
  100. provide/foundation/serialization/core.py +70 -0
  101. provide/foundation/streams/config.py +78 -0
  102. provide/foundation/streams/console.py +4 -5
  103. provide/foundation/streams/core.py +5 -2
  104. provide/foundation/streams/file.py +12 -2
  105. provide/foundation/testing/__init__.py +29 -9
  106. provide/foundation/testing/archive/__init__.py +7 -7
  107. provide/foundation/testing/archive/fixtures.py +58 -54
  108. provide/foundation/testing/cli.py +30 -20
  109. provide/foundation/testing/common/__init__.py +13 -15
  110. provide/foundation/testing/common/fixtures.py +27 -57
  111. provide/foundation/testing/file/__init__.py +15 -15
  112. provide/foundation/testing/file/content_fixtures.py +289 -0
  113. provide/foundation/testing/file/directory_fixtures.py +107 -0
  114. provide/foundation/testing/file/fixtures.py +42 -516
  115. provide/foundation/testing/file/special_fixtures.py +145 -0
  116. provide/foundation/testing/logger.py +89 -8
  117. provide/foundation/testing/mocking/__init__.py +21 -21
  118. provide/foundation/testing/mocking/fixtures.py +80 -67
  119. provide/foundation/testing/process/__init__.py +23 -23
  120. provide/foundation/testing/process/async_fixtures.py +414 -0
  121. provide/foundation/testing/process/fixtures.py +48 -571
  122. provide/foundation/testing/process/subprocess_fixtures.py +210 -0
  123. provide/foundation/testing/threading/__init__.py +17 -17
  124. provide/foundation/testing/threading/basic_fixtures.py +105 -0
  125. provide/foundation/testing/threading/data_fixtures.py +101 -0
  126. provide/foundation/testing/threading/execution_fixtures.py +278 -0
  127. provide/foundation/testing/threading/fixtures.py +32 -502
  128. provide/foundation/testing/threading/sync_fixtures.py +100 -0
  129. provide/foundation/testing/time/__init__.py +11 -11
  130. provide/foundation/testing/time/fixtures.py +95 -83
  131. provide/foundation/testing/transport/__init__.py +9 -9
  132. provide/foundation/testing/transport/fixtures.py +54 -54
  133. provide/foundation/time/__init__.py +18 -0
  134. provide/foundation/time/core.py +63 -0
  135. provide/foundation/tools/__init__.py +2 -2
  136. provide/foundation/tools/base.py +68 -67
  137. provide/foundation/tools/cache.py +69 -74
  138. provide/foundation/tools/downloader.py +68 -62
  139. provide/foundation/tools/installer.py +51 -57
  140. provide/foundation/tools/registry.py +38 -45
  141. provide/foundation/tools/resolver.py +70 -68
  142. provide/foundation/tools/verifier.py +39 -50
  143. provide/foundation/tracer/spans.py +2 -14
  144. provide/foundation/transport/__init__.py +26 -33
  145. provide/foundation/transport/base.py +32 -30
  146. provide/foundation/transport/client.py +44 -49
  147. provide/foundation/transport/config.py +36 -107
  148. provide/foundation/transport/errors.py +13 -27
  149. provide/foundation/transport/http.py +69 -55
  150. provide/foundation/transport/middleware.py +113 -114
  151. provide/foundation/transport/registry.py +29 -27
  152. provide/foundation/transport/types.py +6 -6
  153. provide/foundation/utils/deps.py +17 -14
  154. provide/foundation/utils/parsing.py +49 -4
  155. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
  156. provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
  157. provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
  158. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  159. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  160. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
  161. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
  162. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
  163. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -5,11 +5,11 @@ Fixtures and helpers for testing network operations, including
5
5
  mock servers, free port allocation, and HTTP client mocking.
6
6
  """
7
7
 
8
+ from collections.abc import Generator
9
+ from http.server import BaseHTTPRequestHandler, HTTPServer
8
10
  import socket
9
11
  import threading
10
- from http.server import HTTPServer, BaseHTTPRequestHandler
11
12
  from typing import Any
12
- from collections.abc import Generator
13
13
 
14
14
  import pytest
15
15
 
@@ -18,12 +18,12 @@ import pytest
18
18
  def free_port() -> int:
19
19
  """
20
20
  Get a free port for testing.
21
-
21
+
22
22
  Returns:
23
23
  An available port number on localhost.
24
24
  """
25
25
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
26
- s.bind(('', 0))
26
+ s.bind(("", 0))
27
27
  s.listen(1)
28
28
  port = s.getsockname()[1]
29
29
  return port
@@ -33,62 +33,62 @@ def free_port() -> int:
33
33
  def mock_server(free_port) -> Generator[dict[str, Any], None, None]:
34
34
  """
35
35
  Create a simple mock HTTP server for testing.
36
-
36
+
37
37
  Args:
38
38
  free_port: Free port number from fixture.
39
-
39
+
40
40
  Yields:
41
41
  Dict with server info including url, port, and server instance.
42
42
  """
43
43
  responses = {}
44
44
  requests_received = []
45
-
45
+
46
46
  class MockHandler(BaseHTTPRequestHandler):
47
47
  """Handler for mock HTTP server."""
48
-
48
+
49
49
  def do_GET(self):
50
50
  """Handle GET requests."""
51
- requests_received.append({
52
- "method": "GET",
53
- "path": self.path,
54
- "headers": dict(self.headers)
55
- })
56
-
51
+ requests_received.append(
52
+ {"method": "GET", "path": self.path, "headers": dict(self.headers)}
53
+ )
54
+
57
55
  response = responses.get(self.path, {"status": 404, "body": b"Not Found"})
58
56
  self.send_response(response["status"])
59
57
  for header, value in response.get("headers", {}).items():
60
58
  self.send_header(header, value)
61
59
  self.end_headers()
62
60
  self.wfile.write(response["body"])
63
-
61
+
64
62
  def do_POST(self):
65
63
  """Handle POST requests."""
66
- content_length = int(self.headers.get('Content-Length', 0))
64
+ content_length = int(self.headers.get("Content-Length", 0))
67
65
  body = self.rfile.read(content_length) if content_length else b""
68
-
69
- requests_received.append({
70
- "method": "POST",
71
- "path": self.path,
72
- "headers": dict(self.headers),
73
- "body": body
74
- })
75
-
66
+
67
+ requests_received.append(
68
+ {
69
+ "method": "POST",
70
+ "path": self.path,
71
+ "headers": dict(self.headers),
72
+ "body": body,
73
+ }
74
+ )
75
+
76
76
  response = responses.get(self.path, {"status": 200, "body": b"OK"})
77
77
  self.send_response(response["status"])
78
78
  for header, value in response.get("headers", {}).items():
79
79
  self.send_header(header, value)
80
80
  self.end_headers()
81
81
  self.wfile.write(response["body"])
82
-
82
+
83
83
  def log_message(self, format, *args):
84
84
  """Suppress log messages."""
85
85
  pass
86
-
87
- server = HTTPServer(('localhost', free_port), MockHandler)
86
+
87
+ server = HTTPServer(("localhost", free_port), MockHandler)
88
88
  server_thread = threading.Thread(target=server.serve_forever)
89
89
  server_thread.daemon = True
90
90
  server_thread.start()
91
-
91
+
92
92
  yield {
93
93
  "url": f"http://localhost:{free_port}",
94
94
  "port": free_port,
@@ -96,7 +96,7 @@ def mock_server(free_port) -> Generator[dict[str, Any], None, None]:
96
96
  "responses": responses,
97
97
  "requests": requests_received,
98
98
  }
99
-
99
+
100
100
  server.shutdown()
101
101
  server.server_close()
102
102
 
@@ -105,7 +105,7 @@ def mock_server(free_port) -> Generator[dict[str, Any], None, None]:
105
105
  def httpx_mock_responses():
106
106
  """
107
107
  Pre-configured responses for HTTPX mocking.
108
-
108
+
109
109
  Returns:
110
110
  Dict of common mock responses.
111
111
  """
@@ -142,12 +142,12 @@ def httpx_mock_responses():
142
142
  def mock_websocket():
143
143
  """
144
144
  Mock WebSocket connection for testing.
145
-
145
+
146
146
  Returns:
147
147
  Mock WebSocket with send, receive, close methods.
148
148
  """
149
- from unittest.mock import Mock, AsyncMock
150
-
149
+ from unittest.mock import AsyncMock, Mock
150
+
151
151
  ws = Mock()
152
152
  ws.send = AsyncMock()
153
153
  ws.receive = AsyncMock(return_value={"type": "text", "data": "message"})
@@ -155,11 +155,11 @@ def mock_websocket():
155
155
  ws.accept = AsyncMock()
156
156
  ws.ping = AsyncMock()
157
157
  ws.pong = AsyncMock()
158
-
158
+
159
159
  # State properties
160
160
  ws.closed = False
161
161
  ws.url = "ws://localhost:8000/ws"
162
-
162
+
163
163
  return ws
164
164
 
165
165
 
@@ -167,17 +167,17 @@ def mock_websocket():
167
167
  def mock_dns_resolver():
168
168
  """
169
169
  Mock DNS resolver for testing.
170
-
170
+
171
171
  Returns:
172
172
  Mock resolver with resolve method.
173
173
  """
174
174
  from unittest.mock import Mock
175
-
175
+
176
176
  resolver = Mock()
177
177
  resolver.resolve = Mock(return_value=["127.0.0.1", "::1"])
178
178
  resolver.reverse = Mock(return_value="localhost")
179
179
  resolver.clear_cache = Mock()
180
-
180
+
181
181
  return resolver
182
182
 
183
183
 
@@ -185,39 +185,39 @@ def mock_dns_resolver():
185
185
  def tcp_client_server(free_port) -> Generator[dict[str, Any], None, None]:
186
186
  """
187
187
  Create a TCP client-server pair for testing.
188
-
188
+
189
189
  Yields:
190
190
  Dict with client socket, server socket, and port info.
191
191
  """
192
192
  server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
193
193
  server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
194
- server_socket.bind(('localhost', free_port))
194
+ server_socket.bind(("localhost", free_port))
195
195
  server_socket.listen(1)
196
-
196
+
197
197
  client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
198
-
198
+
199
199
  # Run server accept in thread
200
200
  connection = None
201
-
201
+
202
202
  def accept_connection():
203
203
  nonlocal connection
204
204
  connection, _ = server_socket.accept()
205
-
205
+
206
206
  accept_thread = threading.Thread(target=accept_connection)
207
207
  accept_thread.daemon = True
208
208
  accept_thread.start()
209
-
209
+
210
210
  # Connect client
211
- client_socket.connect(('localhost', free_port))
211
+ client_socket.connect(("localhost", free_port))
212
212
  accept_thread.join(timeout=1)
213
-
213
+
214
214
  yield {
215
215
  "client": client_socket,
216
216
  "server": connection,
217
217
  "server_socket": server_socket,
218
218
  "port": free_port,
219
219
  }
220
-
220
+
221
221
  # Cleanup
222
222
  client_socket.close()
223
223
  if connection:
@@ -229,12 +229,12 @@ def tcp_client_server(free_port) -> Generator[dict[str, Any], None, None]:
229
229
  def mock_ssl_context():
230
230
  """
231
231
  Mock SSL context for testing secure connections.
232
-
232
+
233
233
  Returns:
234
234
  Mock SSL context with common methods.
235
235
  """
236
236
  from unittest.mock import Mock
237
-
237
+
238
238
  context = Mock()
239
239
  context.load_cert_chain = Mock()
240
240
  context.load_verify_locations = Mock()
@@ -242,7 +242,7 @@ def mock_ssl_context():
242
242
  context.wrap_socket = Mock()
243
243
  context.check_hostname = True
244
244
  context.verify_mode = 2 # ssl.CERT_REQUIRED
245
-
245
+
246
246
  return context
247
247
 
248
248
 
@@ -250,7 +250,7 @@ def mock_ssl_context():
250
250
  def network_timeout():
251
251
  """
252
252
  Provide network timeout configuration for tests.
253
-
253
+
254
254
  Returns:
255
255
  Dict with timeout values for different operations.
256
256
  """
@@ -266,7 +266,7 @@ def network_timeout():
266
266
  def mock_http_headers():
267
267
  """
268
268
  Common HTTP headers for testing.
269
-
269
+
270
270
  Returns:
271
271
  Dict of typical HTTP headers.
272
272
  """
@@ -277,4 +277,4 @@ def mock_http_headers():
277
277
  "Authorization": "Bearer test_token",
278
278
  "X-Request-ID": "test-request-123",
279
279
  "X-Correlation-ID": "test-correlation-456",
280
- }
280
+ }
@@ -0,0 +1,18 @@
1
+ """
2
+ Production time utilities for Foundation.
3
+
4
+ Provides consistent time handling with Foundation integration,
5
+ better testability, and timezone awareness.
6
+ """
7
+
8
+ from provide.foundation.time.core import (
9
+ provide_now,
10
+ provide_sleep,
11
+ provide_time,
12
+ )
13
+
14
+ __all__ = [
15
+ "provide_now",
16
+ "provide_sleep",
17
+ "provide_time",
18
+ ]
@@ -0,0 +1,63 @@
1
+ """Core time utilities for Foundation."""
2
+
3
+ from datetime import datetime
4
+ import time
5
+ from zoneinfo import ZoneInfo
6
+
7
+
8
+ def provide_time() -> float:
9
+ """
10
+ Get current time with Foundation tracking.
11
+
12
+ Returns:
13
+ Current time as seconds since epoch
14
+
15
+ Example:
16
+ >>> current_time = provide_time()
17
+ >>> isinstance(current_time, float)
18
+ True
19
+ """
20
+ return time.time()
21
+
22
+
23
+ def provide_sleep(seconds: float) -> None:
24
+ """
25
+ Sleep with Foundation tracking and interruption support.
26
+
27
+ Args:
28
+ seconds: Number of seconds to sleep
29
+
30
+ Example:
31
+ >>> provide_sleep(0.1) # Sleep for 100ms
32
+ """
33
+ if seconds < 0:
34
+ raise ValueError("Sleep duration must be non-negative")
35
+ time.sleep(seconds)
36
+
37
+
38
+ def provide_now(tz: str | ZoneInfo | None = None) -> datetime:
39
+ """
40
+ Get current datetime with timezone awareness.
41
+
42
+ Args:
43
+ tz: Timezone (string name, ZoneInfo object, or None for local)
44
+
45
+ Returns:
46
+ Current datetime with timezone information
47
+
48
+ Example:
49
+ >>> now = provide_now()
50
+ >>> now.tzinfo is not None
51
+ True
52
+ >>> utc_now = provide_now("UTC")
53
+ >>> utc_now.tzinfo.key
54
+ 'UTC'
55
+ """
56
+ if tz is None:
57
+ return datetime.now()
58
+ elif isinstance(tz, str):
59
+ zone = ZoneInfo(tz)
60
+ else:
61
+ zone = tz
62
+
63
+ return datetime.now(zone)
@@ -26,8 +26,8 @@ Example:
26
26
 
27
27
  from provide.foundation.tools.base import (
28
28
  BaseToolManager,
29
- ToolMetadata,
30
29
  ToolError,
30
+ ToolMetadata,
31
31
  )
32
32
  from provide.foundation.tools.cache import ToolCache
33
33
  from provide.foundation.tools.downloader import ToolDownloader
@@ -55,4 +55,4 @@ __all__ = [
55
55
  "get_tool_manager",
56
56
  "get_tool_registry",
57
57
  "register_tool_manager",
58
- ]
58
+ ]