provide-foundation 0.0.0.dev0__py3-none-any.whl → 0.0.0.dev2__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 (161) hide show
  1. provide/foundation/__init__.py +41 -23
  2. provide/foundation/archive/__init__.py +23 -0
  3. provide/foundation/archive/base.py +70 -0
  4. provide/foundation/archive/bzip2.py +157 -0
  5. provide/foundation/archive/gzip.py +159 -0
  6. provide/foundation/archive/operations.py +334 -0
  7. provide/foundation/archive/tar.py +164 -0
  8. provide/foundation/archive/zip.py +203 -0
  9. provide/foundation/cli/__init__.py +2 -2
  10. provide/foundation/cli/commands/deps.py +13 -7
  11. provide/foundation/cli/commands/logs/__init__.py +1 -1
  12. provide/foundation/cli/commands/logs/query.py +1 -1
  13. provide/foundation/cli/commands/logs/send.py +1 -1
  14. provide/foundation/cli/commands/logs/tail.py +1 -1
  15. provide/foundation/cli/decorators.py +11 -10
  16. provide/foundation/cli/main.py +1 -1
  17. provide/foundation/cli/testing.py +2 -35
  18. provide/foundation/cli/utils.py +21 -17
  19. provide/foundation/config/__init__.py +35 -2
  20. provide/foundation/config/base.py +2 -2
  21. provide/foundation/config/converters.py +479 -0
  22. provide/foundation/config/defaults.py +67 -0
  23. provide/foundation/config/env.py +4 -19
  24. provide/foundation/config/loader.py +9 -3
  25. provide/foundation/config/sync.py +19 -4
  26. provide/foundation/console/input.py +5 -5
  27. provide/foundation/console/output.py +35 -13
  28. provide/foundation/context/__init__.py +8 -4
  29. provide/foundation/context/core.py +85 -109
  30. provide/foundation/core.py +1 -2
  31. provide/foundation/crypto/__init__.py +2 -0
  32. provide/foundation/crypto/certificates/__init__.py +34 -0
  33. provide/foundation/crypto/certificates/base.py +173 -0
  34. provide/foundation/crypto/certificates/certificate.py +290 -0
  35. provide/foundation/crypto/certificates/factory.py +213 -0
  36. provide/foundation/crypto/certificates/generator.py +138 -0
  37. provide/foundation/crypto/certificates/loader.py +130 -0
  38. provide/foundation/crypto/certificates/operations.py +198 -0
  39. provide/foundation/crypto/certificates/trust.py +107 -0
  40. provide/foundation/errors/__init__.py +2 -3
  41. provide/foundation/errors/decorators.py +0 -231
  42. provide/foundation/errors/types.py +0 -97
  43. provide/foundation/eventsets/__init__.py +0 -0
  44. provide/foundation/eventsets/display.py +84 -0
  45. provide/foundation/eventsets/registry.py +160 -0
  46. provide/foundation/eventsets/resolver.py +192 -0
  47. provide/foundation/eventsets/sets/das.py +128 -0
  48. provide/foundation/eventsets/sets/database.py +125 -0
  49. provide/foundation/eventsets/sets/http.py +153 -0
  50. provide/foundation/eventsets/sets/llm.py +139 -0
  51. provide/foundation/eventsets/sets/task_queue.py +107 -0
  52. provide/foundation/eventsets/types.py +70 -0
  53. provide/foundation/file/directory.py +13 -22
  54. provide/foundation/file/lock.py +3 -1
  55. provide/foundation/hub/components.py +77 -515
  56. provide/foundation/hub/config.py +151 -0
  57. provide/foundation/hub/discovery.py +62 -0
  58. provide/foundation/hub/handlers.py +81 -0
  59. provide/foundation/hub/lifecycle.py +194 -0
  60. provide/foundation/hub/manager.py +4 -4
  61. provide/foundation/hub/processors.py +44 -0
  62. provide/foundation/integrations/__init__.py +11 -0
  63. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  64. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  65. provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
  66. provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
  67. provide/foundation/integrations/openobserve/config.py +37 -0
  68. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
  70. provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
  71. provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
  72. provide/foundation/logger/__init__.py +3 -10
  73. provide/foundation/logger/config/logging.py +68 -298
  74. provide/foundation/logger/config/telemetry.py +41 -121
  75. provide/foundation/logger/core.py +0 -2
  76. provide/foundation/logger/custom_processors.py +1 -0
  77. provide/foundation/logger/factories.py +11 -2
  78. provide/foundation/logger/processors/main.py +20 -84
  79. provide/foundation/logger/setup/__init__.py +5 -1
  80. provide/foundation/logger/setup/coordinator.py +76 -24
  81. provide/foundation/logger/setup/processors.py +2 -9
  82. provide/foundation/logger/trace.py +27 -0
  83. provide/foundation/metrics/otel.py +10 -10
  84. provide/foundation/observability/__init__.py +2 -2
  85. provide/foundation/process/__init__.py +9 -0
  86. provide/foundation/process/exit.py +47 -0
  87. provide/foundation/process/lifecycle.py +115 -59
  88. provide/foundation/resilience/__init__.py +35 -0
  89. provide/foundation/resilience/circuit.py +164 -0
  90. provide/foundation/resilience/decorators.py +220 -0
  91. provide/foundation/resilience/fallback.py +193 -0
  92. provide/foundation/resilience/retry.py +325 -0
  93. provide/foundation/streams/config.py +79 -0
  94. provide/foundation/streams/console.py +7 -8
  95. provide/foundation/streams/core.py +6 -3
  96. provide/foundation/streams/file.py +12 -2
  97. provide/foundation/testing/__init__.py +84 -2
  98. provide/foundation/testing/archive/__init__.py +24 -0
  99. provide/foundation/testing/archive/fixtures.py +217 -0
  100. provide/foundation/testing/cli.py +30 -17
  101. provide/foundation/testing/common/__init__.py +32 -0
  102. provide/foundation/testing/common/fixtures.py +236 -0
  103. provide/foundation/testing/file/__init__.py +40 -0
  104. provide/foundation/testing/file/content_fixtures.py +316 -0
  105. provide/foundation/testing/file/directory_fixtures.py +107 -0
  106. provide/foundation/testing/file/fixtures.py +52 -0
  107. provide/foundation/testing/file/special_fixtures.py +153 -0
  108. provide/foundation/testing/logger.py +117 -11
  109. provide/foundation/testing/mocking/__init__.py +46 -0
  110. provide/foundation/testing/mocking/fixtures.py +331 -0
  111. provide/foundation/testing/process/__init__.py +48 -0
  112. provide/foundation/testing/process/async_fixtures.py +405 -0
  113. provide/foundation/testing/process/fixtures.py +56 -0
  114. provide/foundation/testing/process/subprocess_fixtures.py +209 -0
  115. provide/foundation/testing/threading/__init__.py +38 -0
  116. provide/foundation/testing/threading/basic_fixtures.py +101 -0
  117. provide/foundation/testing/threading/data_fixtures.py +99 -0
  118. provide/foundation/testing/threading/execution_fixtures.py +263 -0
  119. provide/foundation/testing/threading/fixtures.py +54 -0
  120. provide/foundation/testing/threading/sync_fixtures.py +97 -0
  121. provide/foundation/testing/time/__init__.py +32 -0
  122. provide/foundation/testing/time/fixtures.py +409 -0
  123. provide/foundation/testing/transport/__init__.py +30 -0
  124. provide/foundation/testing/transport/fixtures.py +280 -0
  125. provide/foundation/tools/__init__.py +58 -0
  126. provide/foundation/tools/base.py +348 -0
  127. provide/foundation/tools/cache.py +268 -0
  128. provide/foundation/tools/downloader.py +224 -0
  129. provide/foundation/tools/installer.py +254 -0
  130. provide/foundation/tools/registry.py +223 -0
  131. provide/foundation/tools/resolver.py +321 -0
  132. provide/foundation/tools/verifier.py +186 -0
  133. provide/foundation/tracer/otel.py +7 -11
  134. provide/foundation/tracer/spans.py +2 -2
  135. provide/foundation/transport/__init__.py +155 -0
  136. provide/foundation/transport/base.py +171 -0
  137. provide/foundation/transport/client.py +266 -0
  138. provide/foundation/transport/config.py +140 -0
  139. provide/foundation/transport/errors.py +79 -0
  140. provide/foundation/transport/http.py +232 -0
  141. provide/foundation/transport/middleware.py +360 -0
  142. provide/foundation/transport/registry.py +167 -0
  143. provide/foundation/transport/types.py +45 -0
  144. provide/foundation/utils/deps.py +14 -12
  145. provide/foundation/utils/parsing.py +49 -4
  146. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
  147. provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
  148. provide/foundation/cli/commands/logs/generate_old.py +0 -569
  149. provide/foundation/crypto/certificates.py +0 -896
  150. provide/foundation/logger/emoji/__init__.py +0 -44
  151. provide/foundation/logger/emoji/matrix.py +0 -209
  152. provide/foundation/logger/emoji/sets.py +0 -458
  153. provide/foundation/logger/emoji/types.py +0 -56
  154. provide/foundation/logger/setup/emoji_resolver.py +0 -64
  155. provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
  156. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  157. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  158. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
  159. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
  160. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
  161. {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ """
2
+ Transport configuration with Foundation config integration.
3
+ """
4
+
5
+ from attrs import define
6
+
7
+ from provide.foundation.config.env import RuntimeConfig
8
+ from provide.foundation.config.base import field
9
+ from provide.foundation.config.converters import (
10
+ parse_bool_extended,
11
+ parse_float_with_validation,
12
+ validate_non_negative,
13
+ validate_positive,
14
+ )
15
+ from provide.foundation.config.loader import RuntimeConfigLoader
16
+ from provide.foundation.config.manager import register_config
17
+ from provide.foundation.logger import get_logger
18
+
19
+ log = get_logger(__name__)
20
+
21
+
22
+ @define(slots=True, repr=False)
23
+ class TransportConfig(RuntimeConfig):
24
+ """Base configuration for all transports."""
25
+
26
+ timeout: float = field(
27
+ default=30.0,
28
+ env_var="PROVIDE_TRANSPORT_TIMEOUT",
29
+ converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else 30.0,
30
+ validator=validate_positive,
31
+ description="Request timeout in seconds",
32
+ )
33
+ max_retries: int = field(
34
+ default=3,
35
+ env_var="PROVIDE_TRANSPORT_MAX_RETRIES",
36
+ converter=int,
37
+ validator=validate_non_negative,
38
+ description="Maximum number of retry attempts",
39
+ )
40
+ retry_backoff_factor: float = field(
41
+ default=0.5,
42
+ env_var="PROVIDE_TRANSPORT_RETRY_BACKOFF_FACTOR",
43
+ converter=lambda x: parse_float_with_validation(x, min_val=0.0) if x else 0.5,
44
+ validator=validate_non_negative,
45
+ description="Backoff multiplier for retries",
46
+ )
47
+ verify_ssl: bool = field(
48
+ default=True,
49
+ env_var="PROVIDE_TRANSPORT_VERIFY_SSL",
50
+ converter=parse_bool_extended,
51
+ description="Whether to verify SSL certificates",
52
+ )
53
+
54
+
55
+
56
+ @define(slots=True, repr=False)
57
+ class HTTPConfig(TransportConfig):
58
+ """HTTP-specific configuration."""
59
+
60
+ pool_connections: int = field(
61
+ default=10,
62
+ env_var="PROVIDE_HTTP_POOL_CONNECTIONS",
63
+ converter=int,
64
+ validator=validate_positive,
65
+ description="Number of connection pools to cache",
66
+ )
67
+ pool_maxsize: int = field(
68
+ default=100,
69
+ env_var="PROVIDE_HTTP_POOL_MAXSIZE",
70
+ converter=int,
71
+ validator=validate_positive,
72
+ description="Maximum number of connections per pool",
73
+ )
74
+ follow_redirects: bool = field(
75
+ default=True,
76
+ env_var="PROVIDE_HTTP_FOLLOW_REDIRECTS",
77
+ converter=parse_bool_extended,
78
+ description="Whether to automatically follow redirects",
79
+ )
80
+ http2: bool = field(
81
+ default=False,
82
+ env_var="PROVIDE_HTTP_USE_HTTP2",
83
+ converter=parse_bool_extended,
84
+ description="Enable HTTP/2 support",
85
+ )
86
+ max_redirects: int = field(
87
+ default=5,
88
+ env_var="PROVIDE_HTTP_MAX_REDIRECTS",
89
+ converter=int,
90
+ validator=validate_non_negative,
91
+ description="Maximum number of redirects to follow",
92
+ )
93
+
94
+
95
+
96
+ async def register_transport_configs() -> None:
97
+ """Register transport configurations with the global ConfigManager."""
98
+ try:
99
+ # Register TransportConfig
100
+ await register_config(
101
+ name="transport",
102
+ config=None, # Will be loaded on demand
103
+ loader=RuntimeConfigLoader(prefix="PROVIDE_TRANSPORT"),
104
+ defaults={
105
+ "timeout": 30.0,
106
+ "max_retries": 3,
107
+ "retry_backoff_factor": 0.5,
108
+ "verify_ssl": True,
109
+ }
110
+ )
111
+
112
+ # Register HTTPConfig
113
+ await register_config(
114
+ name="transport.http",
115
+ config=None, # Will be loaded on demand
116
+ loader=RuntimeConfigLoader(prefix="PROVIDE_HTTP"),
117
+ defaults={
118
+ "timeout": 30.0,
119
+ "max_retries": 3,
120
+ "retry_backoff_factor": 0.5,
121
+ "verify_ssl": True,
122
+ "pool_connections": 10,
123
+ "pool_maxsize": 100,
124
+ "follow_redirects": True,
125
+ "http2": False,
126
+ "max_redirects": 5,
127
+ }
128
+ )
129
+
130
+ log.trace("Successfully registered transport configurations with ConfigManager")
131
+
132
+ except Exception as e:
133
+ log.warning("Failed to register transport configurations", error=str(e))
134
+
135
+
136
+ __all__ = [
137
+ "TransportConfig",
138
+ "HTTPConfig",
139
+ "register_transport_configs",
140
+ ]
@@ -0,0 +1,79 @@
1
+ """
2
+ Transport-specific error types.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from provide.foundation.errors.base import FoundationError
8
+
9
+ if TYPE_CHECKING:
10
+ from provide.foundation.transport.base import Request, Response
11
+
12
+
13
+ class TransportError(FoundationError):
14
+ """Base transport error."""
15
+
16
+ def __init__(
17
+ self,
18
+ message: str,
19
+ *,
20
+ request: "Request | None" = None,
21
+ **kwargs
22
+ ):
23
+ super().__init__(message, **kwargs)
24
+ self.request = request
25
+
26
+
27
+ class TransportConnectionError(TransportError):
28
+ """Transport connection failed."""
29
+ pass
30
+
31
+
32
+ class TransportTimeoutError(TransportError):
33
+ """Transport request timed out."""
34
+ pass
35
+
36
+
37
+ class HTTPResponseError(TransportError):
38
+ """HTTP response error (4xx/5xx status codes)."""
39
+
40
+ def __init__(
41
+ self,
42
+ message: str,
43
+ *,
44
+ status_code: int,
45
+ response: "Response",
46
+ **kwargs
47
+ ):
48
+ super().__init__(message, **kwargs)
49
+ self.status_code = status_code
50
+ self.response = response
51
+
52
+
53
+ class TransportConfigurationError(TransportError):
54
+ """Transport configuration error."""
55
+ pass
56
+
57
+
58
+ class TransportNotFoundError(TransportError):
59
+ """No transport found for the given URI scheme."""
60
+
61
+ def __init__(
62
+ self,
63
+ message: str,
64
+ *,
65
+ scheme: str,
66
+ **kwargs
67
+ ):
68
+ super().__init__(message, **kwargs)
69
+ self.scheme = scheme
70
+
71
+
72
+ __all__ = [
73
+ "TransportError",
74
+ "TransportConnectionError",
75
+ "TransportTimeoutError",
76
+ "HTTPResponseError",
77
+ "TransportConfigurationError",
78
+ "TransportNotFoundError",
79
+ ]
@@ -0,0 +1,232 @@
1
+ """
2
+ HTTP/HTTPS transport implementation using httpx.
3
+ """
4
+
5
+ import time
6
+ from collections.abc import AsyncIterator
7
+ from typing import Any
8
+
9
+ import httpx
10
+ from attrs import define, field
11
+
12
+ from provide.foundation.logger import get_logger
13
+ from provide.foundation.transport.base import Request, Response, TransportBase
14
+ from provide.foundation.transport.config import HTTPConfig
15
+ from provide.foundation.transport.errors import (
16
+ HTTPResponseError,
17
+ TransportConnectionError,
18
+ TransportTimeoutError,
19
+ )
20
+ from provide.foundation.transport.types import TransportType
21
+
22
+ log = get_logger(__name__)
23
+
24
+
25
+ @define
26
+ class HTTPTransport(TransportBase):
27
+ """HTTP/HTTPS transport using httpx backend."""
28
+
29
+ SCHEMES = ["http", "https"]
30
+
31
+ config: HTTPConfig = field(factory=HTTPConfig.from_env)
32
+ _client: httpx.AsyncClient | None = field(default=None, init=False)
33
+
34
+ def supports(self, transport_type: TransportType) -> bool:
35
+ """Check if this transport supports the given type."""
36
+ return transport_type.value in self.SCHEMES
37
+
38
+ async def connect(self) -> None:
39
+ """Initialize httpx client with configuration."""
40
+ if self._client is not None:
41
+ return
42
+
43
+ limits = httpx.Limits(
44
+ max_connections=self.config.pool_connections,
45
+ max_keepalive_connections=self.config.pool_maxsize,
46
+ )
47
+
48
+ timeout = httpx.Timeout(self.config.timeout)
49
+
50
+ self._client = httpx.AsyncClient(
51
+ limits=limits,
52
+ timeout=timeout,
53
+ verify=self.config.verify_ssl,
54
+ follow_redirects=self.config.follow_redirects,
55
+ max_redirects=self.config.max_redirects,
56
+ http2=self.config.http2,
57
+ )
58
+
59
+ log.trace("HTTP transport connected",
60
+ pool_connections=self.config.pool_connections,
61
+ http2=self.config.http2)
62
+
63
+ async def disconnect(self) -> None:
64
+ """Close httpx client."""
65
+ if self._client is not None:
66
+ await self._client.aclose()
67
+ self._client = None
68
+ log.trace("HTTP transport disconnected")
69
+
70
+ async def execute(self, request: Request) -> Response:
71
+ """Execute HTTP request."""
72
+ await self.connect()
73
+
74
+ if self._client is None:
75
+ raise TransportConnectionError("HTTP client not connected")
76
+
77
+ # Log request with emoji
78
+ log.info(f"🚀 {request.method} {request.uri}")
79
+
80
+ start_time = time.perf_counter()
81
+
82
+ try:
83
+ # Determine request body format
84
+ json_data = None
85
+ data = None
86
+
87
+ if request.body is not None:
88
+ if isinstance(request.body, dict):
89
+ json_data = request.body
90
+ elif isinstance(request.body, (str, bytes)):
91
+ data = request.body
92
+ else:
93
+ # Try to serialize as JSON
94
+ import json
95
+ json_data = request.body
96
+
97
+ # Make the request
98
+ httpx_response = await self._client.request(
99
+ method=request.method,
100
+ url=request.uri,
101
+ headers=request.headers,
102
+ params=request.params,
103
+ json=json_data,
104
+ data=data,
105
+ timeout=request.timeout or self.config.timeout,
106
+ )
107
+
108
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
109
+
110
+ # Log response with status emoji
111
+ status_emoji = self._get_status_emoji(httpx_response.status_code)
112
+ log.info(f"{status_emoji} {httpx_response.status_code} ({elapsed_ms:.0f}ms)")
113
+
114
+ # Create response object
115
+ response = Response(
116
+ status=httpx_response.status_code,
117
+ headers=dict(httpx_response.headers),
118
+ body=httpx_response.content,
119
+ metadata={
120
+ "http_version": str(httpx_response.http_version),
121
+ "reason_phrase": httpx_response.reason_phrase,
122
+ "encoding": httpx_response.encoding,
123
+ "is_redirect": httpx_response.is_redirect,
124
+ "url": str(httpx_response.url),
125
+ },
126
+ elapsed_ms=elapsed_ms,
127
+ request=request,
128
+ )
129
+
130
+ return response
131
+
132
+ except httpx.ConnectError as e:
133
+ log.error(f"❌ Connection failed: {e}")
134
+ raise TransportConnectionError(f"Failed to connect: {e}", request=request) from e
135
+
136
+ except httpx.TimeoutException as e:
137
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
138
+ log.error(f"⏱️ Request timed out ({elapsed_ms:.0f}ms)")
139
+ raise TransportTimeoutError(f"Request timed out: {e}", request=request) from e
140
+
141
+ except httpx.RequestError as e:
142
+ log.error(f"❌ Request failed: {e}")
143
+ raise TransportConnectionError(f"Request failed: {e}", request=request) from e
144
+
145
+ except Exception as e:
146
+ log.error(f"❌ Unexpected error: {e}", exc_info=True)
147
+ raise TransportConnectionError(f"Unexpected error: {e}", request=request) from e
148
+
149
+ async def stream(self, request: Request) -> AsyncIterator[bytes]:
150
+ """Stream HTTP response."""
151
+ await self.connect()
152
+
153
+ if self._client is None:
154
+ raise TransportConnectionError("HTTP client not connected")
155
+
156
+ log.info(f"🌊 Streaming {request.method} {request.uri}")
157
+
158
+ try:
159
+ async with self._client.stream(
160
+ method=request.method,
161
+ url=request.uri,
162
+ headers=request.headers,
163
+ params=request.params,
164
+ timeout=request.timeout or self.config.timeout,
165
+ ) as response:
166
+
167
+ # Log response start
168
+ status_emoji = self._get_status_emoji(response.status_code)
169
+ log.info(f"{status_emoji} {response.status_code} (streaming)")
170
+
171
+ # Stream the response
172
+ async for chunk in response.aiter_bytes():
173
+ yield chunk
174
+
175
+ except httpx.ConnectError as e:
176
+ raise TransportConnectionError(f"Failed to connect: {e}", request=request) from e
177
+
178
+ except httpx.TimeoutException as e:
179
+ raise TransportTimeoutError(f"Stream timed out: {e}", request=request) from e
180
+
181
+ except httpx.RequestError as e:
182
+ raise TransportConnectionError(f"Stream failed: {e}", request=request) from e
183
+
184
+ def _get_status_emoji(self, status_code: int) -> str:
185
+ """Get emoji for HTTP status code."""
186
+ if 200 <= status_code < 300:
187
+ return "✅" # Success
188
+ elif 300 <= status_code < 400:
189
+ return "↩️" # Redirect
190
+ elif 400 <= status_code < 500:
191
+ return "⚠️" # Client error
192
+ elif 500 <= status_code < 600:
193
+ return "❌" # Server error
194
+ else:
195
+ return "❓" # Unknown
196
+
197
+
198
+ # Auto-register HTTP transport
199
+ def _register_http_transport():
200
+ """Register HTTP transport with the Hub."""
201
+ try:
202
+ from provide.foundation.transport.registry import register_transport
203
+
204
+ register_transport(
205
+ TransportType.HTTP,
206
+ HTTPTransport,
207
+ schemes=HTTPTransport.SCHEMES,
208
+ description="HTTP/HTTPS transport using httpx",
209
+ version="1.0.0",
210
+ )
211
+
212
+ # Also register HTTPS explicitly
213
+ register_transport(
214
+ TransportType.HTTPS,
215
+ HTTPTransport,
216
+ schemes=HTTPTransport.SCHEMES,
217
+ description="HTTP/HTTPS transport using httpx",
218
+ version="1.0.0",
219
+ )
220
+
221
+ except ImportError:
222
+ # Registry not available yet, will be registered later
223
+ pass
224
+
225
+
226
+ # Register when module is imported
227
+ _register_http_transport()
228
+
229
+
230
+ __all__ = [
231
+ "HTTPTransport",
232
+ ]