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.
- provide/foundation/__init__.py +41 -23
- provide/foundation/archive/__init__.py +23 -0
- provide/foundation/archive/base.py +70 -0
- provide/foundation/archive/bzip2.py +157 -0
- provide/foundation/archive/gzip.py +159 -0
- provide/foundation/archive/operations.py +334 -0
- provide/foundation/archive/tar.py +164 -0
- provide/foundation/archive/zip.py +203 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +13 -7
- provide/foundation/cli/commands/logs/__init__.py +1 -1
- provide/foundation/cli/commands/logs/query.py +1 -1
- provide/foundation/cli/commands/logs/send.py +1 -1
- provide/foundation/cli/commands/logs/tail.py +1 -1
- provide/foundation/cli/decorators.py +11 -10
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -35
- provide/foundation/cli/utils.py +21 -17
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +479 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +4 -19
- provide/foundation/config/loader.py +9 -3
- provide/foundation/config/sync.py +19 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +35 -13
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +85 -109
- provide/foundation/core.py +1 -2
- provide/foundation/crypto/__init__.py +2 -0
- provide/foundation/crypto/certificates/__init__.py +34 -0
- provide/foundation/crypto/certificates/base.py +173 -0
- provide/foundation/crypto/certificates/certificate.py +290 -0
- provide/foundation/crypto/certificates/factory.py +213 -0
- provide/foundation/crypto/certificates/generator.py +138 -0
- provide/foundation/crypto/certificates/loader.py +130 -0
- provide/foundation/crypto/certificates/operations.py +198 -0
- provide/foundation/crypto/certificates/trust.py +107 -0
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/eventsets/__init__.py +0 -0
- provide/foundation/eventsets/display.py +84 -0
- provide/foundation/eventsets/registry.py +160 -0
- provide/foundation/eventsets/resolver.py +192 -0
- provide/foundation/eventsets/sets/das.py +128 -0
- provide/foundation/eventsets/sets/database.py +125 -0
- provide/foundation/eventsets/sets/http.py +153 -0
- provide/foundation/eventsets/sets/llm.py +139 -0
- provide/foundation/eventsets/sets/task_queue.py +107 -0
- provide/foundation/eventsets/types.py +70 -0
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +77 -515
- provide/foundation/hub/config.py +151 -0
- provide/foundation/hub/discovery.py +62 -0
- provide/foundation/hub/handlers.py +81 -0
- provide/foundation/hub/lifecycle.py +194 -0
- provide/foundation/hub/manager.py +4 -4
- provide/foundation/hub/processors.py +44 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
- provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
- provide/foundation/logger/__init__.py +3 -10
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/core.py +0 -2
- provide/foundation/logger/custom_processors.py +1 -0
- provide/foundation/logger/factories.py +11 -2
- provide/foundation/logger/processors/main.py +20 -84
- provide/foundation/logger/setup/__init__.py +5 -1
- provide/foundation/logger/setup/coordinator.py +76 -24
- provide/foundation/logger/setup/processors.py +2 -9
- provide/foundation/logger/trace.py +27 -0
- provide/foundation/metrics/otel.py +10 -10
- provide/foundation/observability/__init__.py +2 -2
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +47 -0
- provide/foundation/process/lifecycle.py +115 -59
- provide/foundation/resilience/__init__.py +35 -0
- provide/foundation/resilience/circuit.py +164 -0
- provide/foundation/resilience/decorators.py +220 -0
- provide/foundation/resilience/fallback.py +193 -0
- provide/foundation/resilience/retry.py +325 -0
- provide/foundation/streams/config.py +79 -0
- provide/foundation/streams/console.py +7 -8
- provide/foundation/streams/core.py +6 -3
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +84 -2
- provide/foundation/testing/archive/__init__.py +24 -0
- provide/foundation/testing/archive/fixtures.py +217 -0
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +32 -0
- provide/foundation/testing/common/fixtures.py +236 -0
- provide/foundation/testing/file/__init__.py +40 -0
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +52 -0
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +117 -11
- provide/foundation/testing/mocking/__init__.py +46 -0
- provide/foundation/testing/mocking/fixtures.py +331 -0
- provide/foundation/testing/process/__init__.py +48 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +56 -0
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/__init__.py +38 -0
- provide/foundation/testing/threading/basic_fixtures.py +101 -0
- provide/foundation/testing/threading/data_fixtures.py +99 -0
- provide/foundation/testing/threading/execution_fixtures.py +263 -0
- provide/foundation/testing/threading/fixtures.py +54 -0
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/__init__.py +32 -0
- provide/foundation/testing/time/fixtures.py +409 -0
- provide/foundation/testing/transport/__init__.py +30 -0
- provide/foundation/testing/transport/fixtures.py +280 -0
- provide/foundation/tools/__init__.py +58 -0
- provide/foundation/tools/base.py +348 -0
- provide/foundation/tools/cache.py +268 -0
- provide/foundation/tools/downloader.py +224 -0
- provide/foundation/tools/installer.py +254 -0
- provide/foundation/tools/registry.py +223 -0
- provide/foundation/tools/resolver.py +321 -0
- provide/foundation/tools/verifier.py +186 -0
- provide/foundation/tracer/otel.py +7 -11
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/__init__.py +155 -0
- provide/foundation/transport/base.py +171 -0
- provide/foundation/transport/client.py +266 -0
- provide/foundation/transport/config.py +140 -0
- provide/foundation/transport/errors.py +79 -0
- provide/foundation/transport/http.py +232 -0
- provide/foundation/transport/middleware.py +360 -0
- provide/foundation/transport/registry.py +167 -0
- provide/foundation/transport/types.py +45 -0
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +5 -28
- provide_foundation-0.0.0.dev2.dist-info/RECORD +225 -0
- provide/foundation/cli/commands/logs/generate_old.py +0 -569
- provide/foundation/crypto/certificates.py +0 -896
- provide/foundation/logger/emoji/__init__.py +0 -44
- provide/foundation/logger/emoji/matrix.py +0 -209
- provide/foundation/logger/emoji/sets.py +0 -458
- provide/foundation/logger/emoji/types.py +0 -56
- provide/foundation/logger/setup/emoji_resolver.py +0 -64
- provide_foundation-0.0.0.dev0.dist-info/RECORD +0 -149
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev0.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -6,22 +6,18 @@ components are managed through the Hub registry system. Provides centralized
|
|
6
6
|
component discovery, lifecycle management, and dependency resolution.
|
7
7
|
"""
|
8
8
|
|
9
|
-
import asyncio
|
10
|
-
from enum import Enum
|
11
|
-
import inspect
|
12
9
|
import threading
|
13
|
-
from
|
10
|
+
from enum import Enum
|
11
|
+
from typing import Any, Protocol
|
14
12
|
|
15
13
|
from attrs import define, field
|
16
14
|
|
17
|
-
from provide.foundation.
|
15
|
+
from provide.foundation.errors.decorators import with_error_handling
|
16
|
+
from provide.foundation.hub.registry import Registry
|
18
17
|
from provide.foundation.logger import get_logger
|
19
|
-
from provide.foundation.logger.emoji.types import EmojiSet
|
20
18
|
|
21
19
|
log = get_logger(__name__)
|
22
20
|
|
23
|
-
T = TypeVar("T")
|
24
|
-
|
25
21
|
|
26
22
|
@define(frozen=True, slots=True)
|
27
23
|
class ComponentInfo:
|
@@ -40,12 +36,16 @@ class ComponentInfo:
|
|
40
36
|
class ComponentCategory(Enum):
|
41
37
|
"""Predefined component categories for Foundation."""
|
42
38
|
|
43
|
-
EMOJI_SET = "emoji_set"
|
44
39
|
CONFIG_SOURCE = "config_source"
|
45
40
|
PROCESSOR = "processor"
|
46
41
|
ERROR_HANDLER = "error_handler"
|
47
42
|
FORMATTER = "formatter"
|
48
43
|
FILTER = "filter"
|
44
|
+
TRANSPORT = "transport"
|
45
|
+
TRANSPORT_MIDDLEWARE = "transport.middleware"
|
46
|
+
TRANSPORT_AUTH = "transport.auth"
|
47
|
+
TRANSPORT_CACHE = "transport.cache"
|
48
|
+
EVENT_SET = "eventset"
|
49
49
|
|
50
50
|
|
51
51
|
class ComponentLifecycle(Protocol):
|
@@ -71,304 +71,19 @@ def get_component_registry() -> Registry:
|
|
71
71
|
return _component_registry
|
72
72
|
|
73
73
|
|
74
|
-
|
75
|
-
""
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# Get all emoji sets for this domain
|
80
|
-
all_entries = list(registry)
|
81
|
-
domain_sets = [
|
82
|
-
entry
|
83
|
-
for entry in all_entries
|
84
|
-
if (
|
85
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
86
|
-
and entry.metadata.get("domain") == domain
|
87
|
-
)
|
88
|
-
]
|
89
|
-
|
90
|
-
if not domain_sets:
|
91
|
-
return get_default_emoji_set()
|
92
|
-
|
93
|
-
# Sort by priority (highest first)
|
94
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
95
|
-
return domain_sets[0].value
|
96
|
-
|
97
|
-
|
98
|
-
def get_default_emoji_set() -> EmojiSet:
|
99
|
-
"""Get the default emoji set for unknown domains."""
|
100
|
-
with _registry_lock:
|
101
|
-
registry = get_component_registry()
|
102
|
-
|
103
|
-
# Look for default emoji set
|
104
|
-
all_entries = list(registry)
|
105
|
-
default_sets = [
|
106
|
-
entry
|
107
|
-
for entry in all_entries
|
108
|
-
if (
|
109
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
110
|
-
and entry.metadata.get("default", False)
|
111
|
-
)
|
112
|
-
]
|
113
|
-
|
114
|
-
if default_sets:
|
115
|
-
return default_sets[0].value
|
116
|
-
|
117
|
-
# Return built-in default
|
118
|
-
return EmojiSet(
|
119
|
-
name="default",
|
120
|
-
emojis={
|
121
|
-
"success": "✅",
|
122
|
-
"error": "❌",
|
123
|
-
"info": "ℹ️",
|
124
|
-
"warning": "⚠️",
|
125
|
-
"debug": "🔍",
|
126
|
-
},
|
127
|
-
)
|
128
|
-
|
129
|
-
|
130
|
-
def resolve_emoji_for_domain(domain: str, action: str) -> str:
|
131
|
-
"""Resolve emoji for domain and action with priority ordering."""
|
132
|
-
with _registry_lock:
|
133
|
-
registry = get_component_registry()
|
134
|
-
|
135
|
-
# Get all emoji sets for this domain
|
136
|
-
all_entries = list(registry)
|
137
|
-
domain_sets = [
|
138
|
-
entry
|
139
|
-
for entry in all_entries
|
140
|
-
if (
|
141
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
142
|
-
and entry.metadata.get("domain") == domain
|
143
|
-
)
|
144
|
-
]
|
145
|
-
|
146
|
-
# Sort by priority (highest first)
|
147
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
148
|
-
|
149
|
-
# Try each set in priority order
|
150
|
-
for entry in domain_sets:
|
151
|
-
emoji_set = entry.value
|
152
|
-
if action in emoji_set.emojis:
|
153
|
-
return emoji_set.emojis[action]
|
154
|
-
|
155
|
-
# Fall back to default
|
156
|
-
default_set = get_default_emoji_set()
|
157
|
-
return default_set.emojis.get(action, "📝")
|
158
|
-
|
159
|
-
|
160
|
-
def get_composed_emoji_set(domain: str) -> EmojiSet:
|
161
|
-
"""Get composed emoji set combining all sets for a domain."""
|
162
|
-
with _registry_lock:
|
163
|
-
registry = get_component_registry()
|
164
|
-
|
165
|
-
# Get all emoji sets for this domain
|
166
|
-
all_entries = list(registry)
|
167
|
-
domain_sets = [
|
168
|
-
entry
|
169
|
-
for entry in all_entries
|
170
|
-
if (
|
171
|
-
entry.dimension == ComponentCategory.EMOJI_SET.value
|
172
|
-
and entry.metadata.get("domain") == domain
|
173
|
-
)
|
174
|
-
]
|
175
|
-
|
176
|
-
# Sort by priority (lowest first for composition)
|
177
|
-
domain_sets.sort(key=lambda e: e.metadata.get("priority", 0))
|
178
|
-
|
179
|
-
# Compose emojis
|
180
|
-
composed_emojis = {}
|
181
|
-
for entry in domain_sets:
|
182
|
-
emoji_set = entry.value
|
183
|
-
composed_emojis.update(emoji_set.emojis)
|
184
|
-
|
185
|
-
return EmojiSet(name=f"composed_{domain}", emojis=composed_emojis)
|
186
|
-
|
187
|
-
|
188
|
-
def resolve_config_value(key: str) -> Any:
|
189
|
-
"""Resolve configuration value using priority-ordered sources."""
|
190
|
-
with _registry_lock:
|
191
|
-
registry = get_component_registry()
|
192
|
-
|
193
|
-
# Get all config sources
|
194
|
-
all_entries = list(registry)
|
195
|
-
config_sources = [
|
196
|
-
entry
|
197
|
-
for entry in all_entries
|
198
|
-
if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
|
199
|
-
]
|
200
|
-
|
201
|
-
# Sort by priority (highest first)
|
202
|
-
config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
203
|
-
|
204
|
-
# Try each source
|
205
|
-
for entry in config_sources:
|
206
|
-
source = entry.value
|
207
|
-
if hasattr(source, "get_value"):
|
208
|
-
try:
|
209
|
-
value = source.get_value(key)
|
210
|
-
if value is not None:
|
211
|
-
return value
|
212
|
-
except Exception:
|
213
|
-
continue
|
214
|
-
|
215
|
-
return None
|
216
|
-
|
217
|
-
|
218
|
-
def get_config_chain() -> list[RegistryEntry]:
|
219
|
-
"""Get configuration sources ordered by priority."""
|
220
|
-
with _registry_lock:
|
221
|
-
registry = get_component_registry()
|
222
|
-
|
223
|
-
# Get all config sources
|
224
|
-
all_entries = list(registry)
|
225
|
-
config_sources = [
|
226
|
-
entry
|
227
|
-
for entry in all_entries
|
228
|
-
if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
|
229
|
-
]
|
230
|
-
|
231
|
-
# Sort by priority (highest first)
|
232
|
-
config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
233
|
-
return config_sources
|
234
|
-
|
235
|
-
|
236
|
-
async def load_all_configs() -> dict[str, Any]:
|
237
|
-
"""Load configurations from all registered sources."""
|
238
|
-
configs = {}
|
239
|
-
chain = get_config_chain()
|
240
|
-
|
241
|
-
for entry in chain:
|
242
|
-
source = entry.value
|
243
|
-
if hasattr(source, "load_config"):
|
244
|
-
try:
|
245
|
-
if inspect.iscoroutinefunction(source.load_config):
|
246
|
-
source_config = await source.load_config()
|
247
|
-
else:
|
248
|
-
source_config = source.load_config()
|
249
|
-
|
250
|
-
if source_config:
|
251
|
-
configs.update(source_config)
|
252
|
-
except Exception as e:
|
253
|
-
log.warning(
|
254
|
-
"Config source failed to load", source=entry.name, error=str(e)
|
255
|
-
)
|
256
|
-
|
257
|
-
return configs
|
258
|
-
|
259
|
-
|
260
|
-
def get_processor_pipeline() -> list[RegistryEntry]:
|
261
|
-
"""Get log processors ordered by priority."""
|
262
|
-
with _registry_lock:
|
263
|
-
registry = get_component_registry()
|
264
|
-
|
265
|
-
# Get all processors
|
266
|
-
all_entries = list(registry)
|
267
|
-
processors = [
|
268
|
-
entry
|
269
|
-
for entry in all_entries
|
270
|
-
if entry.dimension == ComponentCategory.PROCESSOR.value
|
271
|
-
]
|
272
|
-
|
273
|
-
# Sort by priority (highest first)
|
274
|
-
processors.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
275
|
-
return processors
|
276
|
-
|
277
|
-
|
278
|
-
def get_processors_for_stage(stage: str) -> list[RegistryEntry]:
|
279
|
-
"""Get processors for a specific processing stage."""
|
280
|
-
pipeline = get_processor_pipeline()
|
281
|
-
return [entry for entry in pipeline if entry.metadata.get("stage") == stage]
|
282
|
-
|
283
|
-
|
284
|
-
def get_handlers_for_exception(exception: Exception) -> list[RegistryEntry]:
|
285
|
-
"""Get error handlers that can handle the given exception type."""
|
286
|
-
with _registry_lock:
|
287
|
-
registry = get_component_registry()
|
288
|
-
|
289
|
-
# Get all error handlers
|
290
|
-
all_entries = list(registry)
|
291
|
-
handlers = [
|
292
|
-
entry
|
293
|
-
for entry in all_entries
|
294
|
-
if entry.dimension == ComponentCategory.ERROR_HANDLER.value
|
295
|
-
]
|
296
|
-
|
297
|
-
# Filter by exception type
|
298
|
-
exception_type_name = type(exception).__name__
|
299
|
-
matching_handlers = []
|
300
|
-
|
301
|
-
for entry in handlers:
|
302
|
-
exception_types = entry.metadata.get("exception_types", [])
|
303
|
-
if any(
|
304
|
-
exc_type in exception_type_name or exception_type_name in exc_type
|
305
|
-
for exc_type in exception_types
|
306
|
-
):
|
307
|
-
matching_handlers.append(entry)
|
308
|
-
|
309
|
-
# Sort by priority (highest first)
|
310
|
-
matching_handlers.sort(
|
311
|
-
key=lambda e: e.metadata.get("priority", 0), reverse=True
|
312
|
-
)
|
313
|
-
return matching_handlers
|
314
|
-
|
315
|
-
|
316
|
-
def execute_error_handlers(
|
317
|
-
exception: Exception, context: dict[str, Any]
|
318
|
-
) -> dict[str, Any] | None:
|
319
|
-
"""Execute error handlers until one handles the exception."""
|
320
|
-
handlers = get_handlers_for_exception(exception)
|
321
|
-
|
322
|
-
for entry in handlers:
|
323
|
-
handler = entry.value
|
324
|
-
try:
|
325
|
-
result = handler(exception, context)
|
326
|
-
if result is not None:
|
327
|
-
return result
|
328
|
-
except Exception as handler_error:
|
329
|
-
log.error(
|
330
|
-
"Error handler failed", handler=entry.name, error=str(handler_error)
|
331
|
-
)
|
332
|
-
|
333
|
-
return None
|
334
|
-
|
335
|
-
|
336
|
-
def resolve_component_dependencies(name: str, dimension: str) -> dict[str, Any]:
|
337
|
-
"""Resolve component dependencies recursively."""
|
338
|
-
with _registry_lock:
|
339
|
-
registry = get_component_registry()
|
340
|
-
entry = registry.get_entry(name, dimension)
|
341
|
-
|
342
|
-
if not entry:
|
343
|
-
return {}
|
344
|
-
|
345
|
-
dependencies = {}
|
346
|
-
dep_names = entry.metadata.get("dependencies", [])
|
347
|
-
|
348
|
-
for dep_name in dep_names:
|
349
|
-
# Try same dimension first
|
350
|
-
dep_component = registry.get(dep_name, dimension)
|
351
|
-
if dep_component is not None:
|
352
|
-
dependencies[dep_name] = dep_component
|
353
|
-
else:
|
354
|
-
# Search across dimensions
|
355
|
-
dep_component = registry.get(dep_name)
|
356
|
-
if dep_component is not None:
|
357
|
-
dependencies[dep_name] = dep_component
|
358
|
-
|
359
|
-
return dependencies
|
360
|
-
|
361
|
-
|
74
|
+
@with_error_handling(
|
75
|
+
fallback={"status": "error"},
|
76
|
+
context_provider=lambda: {"function": "check_component_health", "module": "hub.components"}
|
77
|
+
)
|
362
78
|
def check_component_health(name: str, dimension: str) -> dict[str, Any]:
|
363
79
|
"""Check component health status."""
|
364
80
|
with _registry_lock:
|
365
|
-
|
366
|
-
component = registry.get(name, dimension)
|
81
|
+
component = _component_registry.get(name, dimension)
|
367
82
|
|
368
83
|
if not component:
|
369
84
|
return {"status": "not_found"}
|
370
85
|
|
371
|
-
entry =
|
86
|
+
entry = _component_registry.get_entry(name, dimension)
|
372
87
|
if not entry.metadata.get("supports_health_check", False):
|
373
88
|
return {"status": "no_health_check"}
|
374
89
|
|
@@ -384,8 +99,7 @@ def check_component_health(name: str, dimension: str) -> dict[str, Any]:
|
|
384
99
|
def get_component_config_schema(name: str, dimension: str) -> dict[str, Any] | None:
|
385
100
|
"""Get component configuration schema."""
|
386
101
|
with _registry_lock:
|
387
|
-
|
388
|
-
entry = registry.get_entry(name, dimension)
|
102
|
+
entry = _component_registry.get_entry(name, dimension)
|
389
103
|
|
390
104
|
if not entry:
|
391
105
|
return None
|
@@ -393,166 +107,10 @@ def get_component_config_schema(name: str, dimension: str) -> dict[str, Any] | N
|
|
393
107
|
return entry.metadata.get("config_schema")
|
394
108
|
|
395
109
|
|
396
|
-
def get_or_initialize_component(name: str, dimension: str) -> Any:
|
397
|
-
"""Get component, initializing lazily if needed."""
|
398
|
-
with _registry_lock:
|
399
|
-
key = (name, dimension)
|
400
|
-
|
401
|
-
# Return already initialized component
|
402
|
-
if key in _initialized_components:
|
403
|
-
return _initialized_components[key]
|
404
|
-
|
405
|
-
registry = get_component_registry()
|
406
|
-
entry = registry.get_entry(name, dimension)
|
407
|
-
|
408
|
-
if not entry:
|
409
|
-
return None
|
410
|
-
|
411
|
-
# If already initialized, return it
|
412
|
-
if entry.value is not None:
|
413
|
-
_initialized_components[key] = entry.value
|
414
|
-
return entry.value
|
415
|
-
|
416
|
-
# Initialize lazily
|
417
|
-
if entry.metadata.get("lazy", False):
|
418
|
-
factory = entry.metadata.get("factory")
|
419
|
-
if factory:
|
420
|
-
try:
|
421
|
-
component = factory()
|
422
|
-
# Update registry with initialized component
|
423
|
-
registry.register(
|
424
|
-
name=name,
|
425
|
-
value=component,
|
426
|
-
dimension=dimension,
|
427
|
-
metadata=entry.metadata,
|
428
|
-
replace=True,
|
429
|
-
)
|
430
|
-
_initialized_components[key] = component
|
431
|
-
return component
|
432
|
-
except Exception as e:
|
433
|
-
log.error(
|
434
|
-
"Component initialization failed",
|
435
|
-
component=name,
|
436
|
-
dimension=dimension,
|
437
|
-
error=str(e),
|
438
|
-
)
|
439
|
-
|
440
|
-
return entry.value
|
441
|
-
|
442
|
-
|
443
|
-
async def initialize_async_component(name: str, dimension: str) -> Any:
|
444
|
-
"""Initialize component asynchronously."""
|
445
|
-
with _registry_lock:
|
446
|
-
key = (name, dimension)
|
447
|
-
|
448
|
-
# Return already initialized component
|
449
|
-
if key in _initialized_components:
|
450
|
-
return _initialized_components[key]
|
451
|
-
|
452
|
-
registry = get_component_registry()
|
453
|
-
entry = registry.get_entry(name, dimension)
|
454
|
-
|
455
|
-
if not entry:
|
456
|
-
return None
|
457
|
-
|
458
|
-
# Initialize with async factory
|
459
|
-
if entry.metadata.get("async", False):
|
460
|
-
factory = entry.metadata.get("factory")
|
461
|
-
if factory:
|
462
|
-
try:
|
463
|
-
if inspect.iscoroutinefunction(factory):
|
464
|
-
component = await factory()
|
465
|
-
else:
|
466
|
-
component = factory()
|
467
|
-
|
468
|
-
# Update registry
|
469
|
-
registry.register(
|
470
|
-
name=name,
|
471
|
-
value=component,
|
472
|
-
dimension=dimension,
|
473
|
-
metadata=entry.metadata,
|
474
|
-
replace=True,
|
475
|
-
)
|
476
|
-
_initialized_components[key] = component
|
477
|
-
return component
|
478
|
-
except Exception as e:
|
479
|
-
log.error(
|
480
|
-
"Async component initialization failed",
|
481
|
-
component=name,
|
482
|
-
dimension=dimension,
|
483
|
-
error=str(e),
|
484
|
-
)
|
485
|
-
|
486
|
-
return entry.value
|
487
|
-
|
488
|
-
|
489
|
-
def cleanup_all_components(dimension: str | None = None) -> None:
|
490
|
-
"""Clean up all components in dimension."""
|
491
|
-
with _registry_lock:
|
492
|
-
registry = get_component_registry()
|
493
|
-
|
494
|
-
if dimension:
|
495
|
-
entries = [entry for entry in registry if entry.dimension == dimension]
|
496
|
-
else:
|
497
|
-
entries = list(registry)
|
498
|
-
|
499
|
-
for entry in entries:
|
500
|
-
if entry.metadata.get("supports_cleanup", False):
|
501
|
-
component = entry.value
|
502
|
-
if hasattr(component, "cleanup"):
|
503
|
-
try:
|
504
|
-
cleanup_func = component.cleanup
|
505
|
-
if inspect.iscoroutinefunction(cleanup_func):
|
506
|
-
# Run async cleanup
|
507
|
-
loop = None
|
508
|
-
try:
|
509
|
-
loop = asyncio.get_event_loop()
|
510
|
-
if loop.is_running():
|
511
|
-
# Create task if loop is running
|
512
|
-
loop.create_task(cleanup_func())
|
513
|
-
else:
|
514
|
-
loop.run_until_complete(cleanup_func())
|
515
|
-
except RuntimeError:
|
516
|
-
# Create new loop if none exists
|
517
|
-
loop = asyncio.new_event_loop()
|
518
|
-
loop.run_until_complete(cleanup_func())
|
519
|
-
loop.close()
|
520
|
-
else:
|
521
|
-
cleanup_func()
|
522
|
-
except Exception as e:
|
523
|
-
log.error(
|
524
|
-
"Component cleanup failed",
|
525
|
-
component=entry.name,
|
526
|
-
dimension=entry.dimension,
|
527
|
-
error=str(e),
|
528
|
-
)
|
529
|
-
|
530
|
-
|
531
110
|
def bootstrap_foundation() -> None:
|
532
111
|
"""Bootstrap Foundation with core registry components."""
|
533
112
|
registry = get_component_registry()
|
534
113
|
|
535
|
-
# Register default emoji set
|
536
|
-
default_emoji_set = EmojiSet(
|
537
|
-
name="foundation_default",
|
538
|
-
emojis={
|
539
|
-
"success": "✅",
|
540
|
-
"error": "❌",
|
541
|
-
"info": "ℹ️",
|
542
|
-
"warning": "⚠️",
|
543
|
-
"debug": "🔍",
|
544
|
-
},
|
545
|
-
)
|
546
|
-
|
547
|
-
registry.register(
|
548
|
-
name="default",
|
549
|
-
value=default_emoji_set,
|
550
|
-
dimension=ComponentCategory.EMOJI_SET.value,
|
551
|
-
metadata={"default": True, "priority": 1},
|
552
|
-
)
|
553
|
-
|
554
|
-
# Config sources would be registered here when implemented
|
555
|
-
|
556
114
|
# Register core processors
|
557
115
|
def timestamp_processor(logger, method_name, event_dict):
|
558
116
|
import time
|
@@ -570,71 +128,75 @@ def bootstrap_foundation() -> None:
|
|
570
128
|
log.debug("Foundation bootstrap completed with registry components")
|
571
129
|
|
572
130
|
|
573
|
-
def load_config_from_registry(config_class: type[T]) -> T:
|
574
|
-
"""Load configuration using registered config sources."""
|
575
|
-
configs = {}
|
576
|
-
|
577
|
-
# Use sync version for now - async version needs event loop handling
|
578
|
-
chain = get_config_chain()
|
579
|
-
for entry in chain:
|
580
|
-
source = entry.value
|
581
|
-
if hasattr(source, "load_config"):
|
582
|
-
try:
|
583
|
-
if not inspect.iscoroutinefunction(source.load_config):
|
584
|
-
source_config = source.load_config()
|
585
|
-
if source_config:
|
586
|
-
configs.update(source_config)
|
587
|
-
except Exception as e:
|
588
|
-
log.warning("Config source failed", source=entry.name, error=str(e))
|
589
|
-
|
590
|
-
return config_class.from_dict(configs)
|
591
|
-
|
592
|
-
|
593
|
-
async def initialize_all_async_components() -> None:
|
594
|
-
"""Initialize all async components in dependency order."""
|
595
|
-
registry = get_component_registry()
|
596
|
-
|
597
|
-
# Get all async components
|
598
|
-
async_components = [
|
599
|
-
entry for entry in registry if entry.metadata.get("async", False)
|
600
|
-
]
|
601
|
-
|
602
|
-
# Sort by priority for initialization order
|
603
|
-
async_components.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
604
|
-
|
605
|
-
# Initialize each component
|
606
|
-
for entry in async_components:
|
607
|
-
try:
|
608
|
-
await initialize_async_component(entry.name, entry.dimension)
|
609
|
-
except Exception as e:
|
610
|
-
log.error(
|
611
|
-
"Failed to initialize async component",
|
612
|
-
component=entry.name,
|
613
|
-
dimension=entry.dimension,
|
614
|
-
error=str(e),
|
615
|
-
)
|
616
|
-
|
617
|
-
|
618
131
|
def reset_registry_for_tests() -> None:
|
619
|
-
"""Reset registry state for
|
132
|
+
"""Reset registry state for testing."""
|
133
|
+
global _initialized_components
|
620
134
|
with _registry_lock:
|
621
|
-
global _initialized_components
|
622
135
|
_component_registry.clear()
|
623
136
|
_initialized_components.clear()
|
624
137
|
|
625
138
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
139
|
+
# Import and re-export functions from specialized modules
|
140
|
+
from provide.foundation.hub.config import (
|
141
|
+
resolve_config_value,
|
142
|
+
get_config_chain,
|
143
|
+
load_all_configs,
|
144
|
+
load_config_from_registry,
|
145
|
+
)
|
633
146
|
|
634
|
-
|
635
|
-
|
636
|
-
|
147
|
+
from provide.foundation.hub.handlers import (
|
148
|
+
get_handlers_for_exception,
|
149
|
+
execute_error_handlers,
|
150
|
+
)
|
151
|
+
|
152
|
+
from provide.foundation.hub.lifecycle import (
|
153
|
+
get_or_initialize_component,
|
154
|
+
initialize_async_component,
|
155
|
+
cleanup_all_components,
|
156
|
+
initialize_all_async_components,
|
157
|
+
)
|
158
|
+
|
159
|
+
from provide.foundation.hub.processors import (
|
160
|
+
get_processor_pipeline,
|
161
|
+
get_processors_for_stage,
|
162
|
+
)
|
163
|
+
|
164
|
+
from provide.foundation.hub.discovery import (
|
165
|
+
resolve_component_dependencies,
|
166
|
+
discover_components,
|
167
|
+
)
|
637
168
|
|
638
169
|
|
639
170
|
# Bootstrap on module import
|
640
171
|
bootstrap_foundation()
|
172
|
+
|
173
|
+
|
174
|
+
__all__ = [
|
175
|
+
# Core classes
|
176
|
+
"ComponentInfo",
|
177
|
+
"ComponentCategory",
|
178
|
+
"ComponentLifecycle",
|
179
|
+
# Registry access
|
180
|
+
"get_component_registry",
|
181
|
+
# Health and schema
|
182
|
+
"check_component_health",
|
183
|
+
"get_component_config_schema",
|
184
|
+
# Bootstrap and testing
|
185
|
+
"bootstrap_foundation",
|
186
|
+
"reset_registry_for_tests",
|
187
|
+
# Re-exported from specialized modules
|
188
|
+
"resolve_config_value",
|
189
|
+
"get_config_chain",
|
190
|
+
"load_all_configs",
|
191
|
+
"load_config_from_registry",
|
192
|
+
"get_handlers_for_exception",
|
193
|
+
"execute_error_handlers",
|
194
|
+
"get_or_initialize_component",
|
195
|
+
"initialize_async_component",
|
196
|
+
"cleanup_all_components",
|
197
|
+
"initialize_all_async_components",
|
198
|
+
"get_processor_pipeline",
|
199
|
+
"get_processors_for_stage",
|
200
|
+
"resolve_component_dependencies",
|
201
|
+
"discover_components",
|
202
|
+
]
|