provide-foundation 0.0.0.dev1__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 (93) hide show
  1. provide/foundation/__init__.py +29 -3
  2. provide/foundation/archive/operations.py +4 -6
  3. provide/foundation/cli/__init__.py +2 -2
  4. provide/foundation/cli/commands/deps.py +13 -7
  5. provide/foundation/cli/commands/logs/__init__.py +1 -1
  6. provide/foundation/cli/commands/logs/query.py +1 -1
  7. provide/foundation/cli/commands/logs/send.py +1 -1
  8. provide/foundation/cli/commands/logs/tail.py +1 -1
  9. provide/foundation/cli/decorators.py +11 -10
  10. provide/foundation/cli/main.py +1 -1
  11. provide/foundation/cli/testing.py +2 -35
  12. provide/foundation/cli/utils.py +21 -17
  13. provide/foundation/config/__init__.py +35 -2
  14. provide/foundation/config/converters.py +479 -0
  15. provide/foundation/config/defaults.py +67 -0
  16. provide/foundation/config/env.py +4 -19
  17. provide/foundation/config/loader.py +9 -3
  18. provide/foundation/console/input.py +5 -5
  19. provide/foundation/console/output.py +35 -13
  20. provide/foundation/context/__init__.py +8 -4
  21. provide/foundation/context/core.py +85 -109
  22. provide/foundation/crypto/certificates/operations.py +1 -1
  23. provide/foundation/errors/__init__.py +2 -3
  24. provide/foundation/errors/decorators.py +0 -231
  25. provide/foundation/errors/types.py +0 -97
  26. provide/foundation/file/directory.py +13 -22
  27. provide/foundation/file/lock.py +3 -1
  28. provide/foundation/hub/components.py +72 -384
  29. provide/foundation/hub/config.py +151 -0
  30. provide/foundation/hub/discovery.py +62 -0
  31. provide/foundation/hub/handlers.py +81 -0
  32. provide/foundation/hub/lifecycle.py +194 -0
  33. provide/foundation/hub/manager.py +4 -4
  34. provide/foundation/hub/processors.py +44 -0
  35. provide/foundation/integrations/__init__.py +11 -0
  36. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  37. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  38. provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
  39. provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
  40. provide/foundation/integrations/openobserve/config.py +37 -0
  41. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  42. provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
  43. provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
  44. provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
  45. provide/foundation/logger/config/logging.py +68 -298
  46. provide/foundation/logger/config/telemetry.py +41 -121
  47. provide/foundation/logger/setup/coordinator.py +1 -1
  48. provide/foundation/observability/__init__.py +2 -2
  49. provide/foundation/process/__init__.py +9 -0
  50. provide/foundation/process/exit.py +47 -0
  51. provide/foundation/process/lifecycle.py +33 -33
  52. provide/foundation/resilience/__init__.py +35 -0
  53. provide/foundation/resilience/circuit.py +164 -0
  54. provide/foundation/resilience/decorators.py +220 -0
  55. provide/foundation/resilience/fallback.py +193 -0
  56. provide/foundation/resilience/retry.py +325 -0
  57. provide/foundation/streams/config.py +79 -0
  58. provide/foundation/streams/console.py +7 -8
  59. provide/foundation/streams/core.py +6 -3
  60. provide/foundation/streams/file.py +12 -2
  61. provide/foundation/testing/__init__.py +7 -2
  62. provide/foundation/testing/cli.py +30 -17
  63. provide/foundation/testing/common/__init__.py +0 -2
  64. provide/foundation/testing/common/fixtures.py +0 -27
  65. provide/foundation/testing/file/content_fixtures.py +316 -0
  66. provide/foundation/testing/file/directory_fixtures.py +107 -0
  67. provide/foundation/testing/file/fixtures.py +45 -516
  68. provide/foundation/testing/file/special_fixtures.py +153 -0
  69. provide/foundation/testing/logger.py +76 -0
  70. provide/foundation/testing/process/async_fixtures.py +405 -0
  71. provide/foundation/testing/process/fixtures.py +50 -571
  72. provide/foundation/testing/process/subprocess_fixtures.py +209 -0
  73. provide/foundation/testing/threading/basic_fixtures.py +101 -0
  74. provide/foundation/testing/threading/data_fixtures.py +99 -0
  75. provide/foundation/testing/threading/execution_fixtures.py +263 -0
  76. provide/foundation/testing/threading/fixtures.py +34 -500
  77. provide/foundation/testing/threading/sync_fixtures.py +97 -0
  78. provide/foundation/testing/time/fixtures.py +4 -4
  79. provide/foundation/tools/cache.py +8 -6
  80. provide/foundation/tools/downloader.py +23 -12
  81. provide/foundation/tracer/spans.py +2 -2
  82. provide/foundation/transport/config.py +26 -95
  83. provide/foundation/transport/middleware.py +30 -36
  84. provide/foundation/utils/deps.py +14 -12
  85. provide/foundation/utils/parsing.py +49 -4
  86. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +1 -1
  87. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/RECORD +93 -68
  88. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  89. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  90. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/WHEEL +0 -0
  91. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
  92. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
  93. {provide_foundation-0.0.0.dev1.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 typing import Any, Protocol, TypeVar
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.hub.registry import Registry, RegistryEntry
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.eventsets.types import EventMapping
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:
@@ -75,191 +71,19 @@ def get_component_registry() -> Registry:
75
71
  return _component_registry
76
72
 
77
73
 
78
-
79
- def resolve_config_value(key: str) -> Any:
80
- """Resolve configuration value using priority-ordered sources."""
81
- with _registry_lock:
82
- registry = get_component_registry()
83
-
84
- # Get all config sources
85
- all_entries = list(registry)
86
- config_sources = [
87
- entry
88
- for entry in all_entries
89
- if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
90
- ]
91
-
92
- # Sort by priority (highest first)
93
- config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
94
-
95
- # Try each source
96
- for entry in config_sources:
97
- source = entry.value
98
- if hasattr(source, "get_value"):
99
- try:
100
- value = source.get_value(key)
101
- if value is not None:
102
- return value
103
- except Exception:
104
- continue
105
-
106
- return None
107
-
108
-
109
- def get_config_chain() -> list[RegistryEntry]:
110
- """Get configuration sources ordered by priority."""
111
- with _registry_lock:
112
- registry = get_component_registry()
113
-
114
- # Get all config sources
115
- all_entries = list(registry)
116
- config_sources = [
117
- entry
118
- for entry in all_entries
119
- if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
120
- ]
121
-
122
- # Sort by priority (highest first)
123
- config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
124
- return config_sources
125
-
126
-
127
- async def load_all_configs() -> dict[str, Any]:
128
- """Load configurations from all registered sources."""
129
- configs = {}
130
- chain = get_config_chain()
131
-
132
- for entry in chain:
133
- source = entry.value
134
- if hasattr(source, "load_config"):
135
- try:
136
- if inspect.iscoroutinefunction(source.load_config):
137
- source_config = await source.load_config()
138
- else:
139
- source_config = source.load_config()
140
-
141
- if source_config:
142
- configs.update(source_config)
143
- except Exception as e:
144
- log.warning(
145
- "Config source failed to load", source=entry.name, error=str(e)
146
- )
147
-
148
- return configs
149
-
150
-
151
- def get_processor_pipeline() -> list[RegistryEntry]:
152
- """Get log processors ordered by priority."""
153
- with _registry_lock:
154
- registry = get_component_registry()
155
-
156
- # Get all processors
157
- all_entries = list(registry)
158
- processors = [
159
- entry
160
- for entry in all_entries
161
- if entry.dimension == ComponentCategory.PROCESSOR.value
162
- ]
163
-
164
- # Sort by priority (highest first)
165
- processors.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
166
- return processors
167
-
168
-
169
- def get_processors_for_stage(stage: str) -> list[RegistryEntry]:
170
- """Get processors for a specific processing stage."""
171
- pipeline = get_processor_pipeline()
172
- return [entry for entry in pipeline if entry.metadata.get("stage") == stage]
173
-
174
-
175
- def get_handlers_for_exception(exception: Exception) -> list[RegistryEntry]:
176
- """Get error handlers that can handle the given exception type."""
177
- with _registry_lock:
178
- registry = get_component_registry()
179
-
180
- # Get all error handlers
181
- all_entries = list(registry)
182
- handlers = [
183
- entry
184
- for entry in all_entries
185
- if entry.dimension == ComponentCategory.ERROR_HANDLER.value
186
- ]
187
-
188
- # Filter by exception type
189
- exception_type_name = type(exception).__name__
190
- matching_handlers = []
191
-
192
- for entry in handlers:
193
- exception_types = entry.metadata.get("exception_types", [])
194
- if any(
195
- exc_type in exception_type_name or exception_type_name in exc_type
196
- for exc_type in exception_types
197
- ):
198
- matching_handlers.append(entry)
199
-
200
- # Sort by priority (highest first)
201
- matching_handlers.sort(
202
- key=lambda e: e.metadata.get("priority", 0), reverse=True
203
- )
204
- return matching_handlers
205
-
206
-
207
- def execute_error_handlers(
208
- exception: Exception, context: dict[str, Any]
209
- ) -> dict[str, Any] | None:
210
- """Execute error handlers until one handles the exception."""
211
- handlers = get_handlers_for_exception(exception)
212
-
213
- for entry in handlers:
214
- handler = entry.value
215
- try:
216
- result = handler(exception, context)
217
- if result is not None:
218
- return result
219
- except Exception as handler_error:
220
- log.error(
221
- "Error handler failed", handler=entry.name, error=str(handler_error)
222
- )
223
-
224
- return None
225
-
226
-
227
- def resolve_component_dependencies(name: str, dimension: str) -> dict[str, Any]:
228
- """Resolve component dependencies recursively."""
229
- with _registry_lock:
230
- registry = get_component_registry()
231
- entry = registry.get_entry(name, dimension)
232
-
233
- if not entry:
234
- return {}
235
-
236
- dependencies = {}
237
- dep_names = entry.metadata.get("dependencies", [])
238
-
239
- for dep_name in dep_names:
240
- # Try same dimension first
241
- dep_component = registry.get(dep_name, dimension)
242
- if dep_component is not None:
243
- dependencies[dep_name] = dep_component
244
- else:
245
- # Search across dimensions
246
- dep_component = registry.get(dep_name)
247
- if dep_component is not None:
248
- dependencies[dep_name] = dep_component
249
-
250
- return dependencies
251
-
252
-
74
+ @with_error_handling(
75
+ fallback={"status": "error"},
76
+ context_provider=lambda: {"function": "check_component_health", "module": "hub.components"}
77
+ )
253
78
  def check_component_health(name: str, dimension: str) -> dict[str, Any]:
254
79
  """Check component health status."""
255
80
  with _registry_lock:
256
- registry = get_component_registry()
257
- component = registry.get(name, dimension)
81
+ component = _component_registry.get(name, dimension)
258
82
 
259
83
  if not component:
260
84
  return {"status": "not_found"}
261
85
 
262
- entry = registry.get_entry(name, dimension)
86
+ entry = _component_registry.get_entry(name, dimension)
263
87
  if not entry.metadata.get("supports_health_check", False):
264
88
  return {"status": "no_health_check"}
265
89
 
@@ -275,8 +99,7 @@ def check_component_health(name: str, dimension: str) -> dict[str, Any]:
275
99
  def get_component_config_schema(name: str, dimension: str) -> dict[str, Any] | None:
276
100
  """Get component configuration schema."""
277
101
  with _registry_lock:
278
- registry = get_component_registry()
279
- entry = registry.get_entry(name, dimension)
102
+ entry = _component_registry.get_entry(name, dimension)
280
103
 
281
104
  if not entry:
282
105
  return None
@@ -284,149 +107,10 @@ def get_component_config_schema(name: str, dimension: str) -> dict[str, Any] | N
284
107
  return entry.metadata.get("config_schema")
285
108
 
286
109
 
287
- def get_or_initialize_component(name: str, dimension: str) -> Any:
288
- """Get component, initializing lazily if needed."""
289
- with _registry_lock:
290
- key = (name, dimension)
291
-
292
- # Return already initialized component
293
- if key in _initialized_components:
294
- return _initialized_components[key]
295
-
296
- registry = get_component_registry()
297
- entry = registry.get_entry(name, dimension)
298
-
299
- if not entry:
300
- return None
301
-
302
- # If already initialized, return it
303
- if entry.value is not None:
304
- _initialized_components[key] = entry.value
305
- return entry.value
306
-
307
- # Initialize lazily
308
- if entry.metadata.get("lazy", False):
309
- factory = entry.metadata.get("factory")
310
- if factory:
311
- try:
312
- component = factory()
313
- # Update registry with initialized component
314
- registry.register(
315
- name=name,
316
- value=component,
317
- dimension=dimension,
318
- metadata=entry.metadata,
319
- replace=True,
320
- )
321
- _initialized_components[key] = component
322
- return component
323
- except Exception as e:
324
- log.error(
325
- "Component initialization failed",
326
- component=name,
327
- dimension=dimension,
328
- error=str(e),
329
- )
330
-
331
- return entry.value
332
-
333
-
334
- async def initialize_async_component(name: str, dimension: str) -> Any:
335
- """Initialize component asynchronously."""
336
- with _registry_lock:
337
- key = (name, dimension)
338
-
339
- # Return already initialized component
340
- if key in _initialized_components:
341
- return _initialized_components[key]
342
-
343
- registry = get_component_registry()
344
- entry = registry.get_entry(name, dimension)
345
-
346
- if not entry:
347
- return None
348
-
349
- # Initialize with async factory
350
- if entry.metadata.get("async", False):
351
- factory = entry.metadata.get("factory")
352
- if factory:
353
- try:
354
- if inspect.iscoroutinefunction(factory):
355
- component = await factory()
356
- else:
357
- component = factory()
358
-
359
- # Update registry
360
- registry.register(
361
- name=name,
362
- value=component,
363
- dimension=dimension,
364
- metadata=entry.metadata,
365
- replace=True,
366
- )
367
- _initialized_components[key] = component
368
- return component
369
- except Exception as e:
370
- log.error(
371
- "Async component initialization failed",
372
- component=name,
373
- dimension=dimension,
374
- error=str(e),
375
- )
376
-
377
- return entry.value
378
-
379
-
380
- def cleanup_all_components(dimension: str | None = None) -> None:
381
- """Clean up all components in dimension."""
382
- with _registry_lock:
383
- registry = get_component_registry()
384
-
385
- if dimension:
386
- entries = [entry for entry in registry if entry.dimension == dimension]
387
- else:
388
- entries = list(registry)
389
-
390
- for entry in entries:
391
- if entry.metadata.get("supports_cleanup", False):
392
- component = entry.value
393
- if hasattr(component, "cleanup"):
394
- try:
395
- cleanup_func = component.cleanup
396
- if inspect.iscoroutinefunction(cleanup_func):
397
- # Run async cleanup
398
- loop = None
399
- try:
400
- loop = asyncio.get_event_loop()
401
- if loop.is_running():
402
- # Create task if loop is running
403
- loop.create_task(cleanup_func())
404
- else:
405
- loop.run_until_complete(cleanup_func())
406
- except RuntimeError:
407
- # Create new loop if none exists
408
- loop = asyncio.new_event_loop()
409
- loop.run_until_complete(cleanup_func())
410
- loop.close()
411
- else:
412
- cleanup_func()
413
- except Exception as e:
414
- log.error(
415
- "Component cleanup failed",
416
- component=entry.name,
417
- dimension=entry.dimension,
418
- error=str(e),
419
- )
420
-
421
-
422
110
  def bootstrap_foundation() -> None:
423
111
  """Bootstrap Foundation with core registry components."""
424
112
  registry = get_component_registry()
425
113
 
426
- # Event sets are now managed by the eventsets module
427
-
428
- # Config sources would be registered here when implemented
429
-
430
114
  # Register core processors
431
115
  def timestamp_processor(logger, method_name, event_dict):
432
116
  import time
@@ -444,71 +128,75 @@ def bootstrap_foundation() -> None:
444
128
  log.debug("Foundation bootstrap completed with registry components")
445
129
 
446
130
 
447
- def load_config_from_registry(config_class: type[T]) -> T:
448
- """Load configuration using registered config sources."""
449
- configs = {}
450
-
451
- # Use sync version for now - async version needs event loop handling
452
- chain = get_config_chain()
453
- for entry in chain:
454
- source = entry.value
455
- if hasattr(source, "load_config"):
456
- try:
457
- if not inspect.iscoroutinefunction(source.load_config):
458
- source_config = source.load_config()
459
- if source_config:
460
- configs.update(source_config)
461
- except Exception as e:
462
- log.warning("Config source failed", source=entry.name, error=str(e))
463
-
464
- return config_class.from_dict(configs)
465
-
466
-
467
- async def initialize_all_async_components() -> None:
468
- """Initialize all async components in dependency order."""
469
- registry = get_component_registry()
470
-
471
- # Get all async components
472
- async_components = [
473
- entry for entry in registry if entry.metadata.get("async", False)
474
- ]
475
-
476
- # Sort by priority for initialization order
477
- async_components.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
478
-
479
- # Initialize each component
480
- for entry in async_components:
481
- try:
482
- await initialize_async_component(entry.name, entry.dimension)
483
- except Exception as e:
484
- log.error(
485
- "Failed to initialize async component",
486
- component=entry.name,
487
- dimension=entry.dimension,
488
- error=str(e),
489
- )
490
-
491
-
492
131
  def reset_registry_for_tests() -> None:
493
- """Reset registry state for test isolation."""
132
+ """Reset registry state for testing."""
133
+ global _initialized_components
494
134
  with _registry_lock:
495
- global _initialized_components
496
135
  _component_registry.clear()
497
136
  _initialized_components.clear()
498
137
 
499
138
 
500
- def discover_components(
501
- group: str,
502
- dimension: str = "component",
503
- registry: Registry | None = None,
504
- ) -> dict[str, type[Any]]:
505
- """
506
- Discover and register components from entry points.
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
+ )
146
+
147
+ from provide.foundation.hub.handlers import (
148
+ get_handlers_for_exception,
149
+ execute_error_handlers,
150
+ )
507
151
 
508
- This is a stub for the TDD implementation.
509
- """
510
- return {}
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
+ )
511
168
 
512
169
 
513
170
  # Bootstrap on module import
514
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
+ ]
@@ -0,0 +1,151 @@
1
+ """
2
+ Hub configuration management utilities.
3
+
4
+ Provides functions for resolving configuration values from registered sources,
5
+ loading configurations, and managing the configuration chain.
6
+ """
7
+
8
+ import asyncio
9
+ import inspect
10
+ from typing import Any, TypeVar
11
+
12
+ from provide.foundation.errors.decorators import with_error_handling
13
+ from provide.foundation.hub.registry import RegistryEntry
14
+ from provide.foundation.logger import get_logger
15
+
16
+ T = TypeVar("T")
17
+
18
+ log = get_logger(__name__)
19
+
20
+
21
+ def _get_registry_and_lock():
22
+ """Get registry and lock from components module."""
23
+ from provide.foundation.hub.components import get_component_registry, _registry_lock, ComponentCategory
24
+ return get_component_registry(), _registry_lock, ComponentCategory
25
+
26
+
27
+ @with_error_handling(fallback=None, suppress=(Exception,))
28
+ def resolve_config_value(key: str) -> Any:
29
+ """Resolve configuration value using priority-ordered sources."""
30
+ registry, registry_lock, ComponentCategory = _get_registry_and_lock()
31
+
32
+ with registry_lock:
33
+ # Get all config sources
34
+ all_entries = list(registry)
35
+ config_sources = [
36
+ entry
37
+ for entry in all_entries
38
+ if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
39
+ ]
40
+
41
+ # Sort by priority (highest first)
42
+ config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
43
+
44
+ # Try each source
45
+ for entry in config_sources:
46
+ source = entry.value
47
+ if hasattr(source, "get_value"):
48
+ # Try to get value, continue on error
49
+ try:
50
+ value = source.get_value(key)
51
+ if value is not None:
52
+ return value
53
+ except Exception:
54
+ continue
55
+
56
+ return None
57
+
58
+
59
+ def get_config_chain() -> list[RegistryEntry]:
60
+ """Get configuration sources ordered by priority."""
61
+ registry, registry_lock, ComponentCategory = _get_registry_and_lock()
62
+
63
+ with registry_lock:
64
+ # Get all config sources
65
+ all_entries = list(registry)
66
+ config_sources = [
67
+ entry
68
+ for entry in all_entries
69
+ if entry.dimension == ComponentCategory.CONFIG_SOURCE.value
70
+ ]
71
+
72
+ # Sort by priority (highest first)
73
+ config_sources.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
74
+ return config_sources
75
+
76
+
77
+ @with_error_handling(
78
+ fallback={},
79
+ context_provider=lambda: {"function": "load_all_configs"}
80
+ )
81
+ async def load_all_configs() -> dict[str, Any]:
82
+ """Load configurations from all registered sources."""
83
+ configs = {}
84
+ chain = get_config_chain()
85
+
86
+ for entry in chain:
87
+ source = entry.value
88
+ if hasattr(source, "load_config"):
89
+ try:
90
+ if inspect.iscoroutinefunction(source.load_config):
91
+ source_config = await source.load_config()
92
+ else:
93
+ source_config = source.load_config()
94
+
95
+ if source_config:
96
+ configs.update(source_config)
97
+ except Exception as e:
98
+ log.warning(
99
+ "Config source failed to load", source=entry.name, error=str(e)
100
+ )
101
+
102
+ return configs
103
+
104
+
105
+ def load_config_from_registry(config_class: type[T]) -> T:
106
+ """
107
+ Load configuration from registry sources.
108
+
109
+ Args:
110
+ config_class: Configuration class to instantiate
111
+
112
+ Returns:
113
+ Configuration instance loaded from registry sources
114
+ """
115
+ registry, registry_lock, ComponentCategory = _get_registry_and_lock()
116
+
117
+ with registry_lock:
118
+ # Get configuration data from registry
119
+ config_data = {}
120
+
121
+ # Load from all config sources
122
+ chain = get_config_chain()
123
+ for entry in chain:
124
+ source = entry.value
125
+ if hasattr(source, "load_config"):
126
+ try:
127
+ # Skip async sources in sync context
128
+ if inspect.iscoroutinefunction(source.load_config):
129
+ log.debug("Skipping async config source in sync context", source=entry.name)
130
+ continue
131
+
132
+ source_data = source.load_config()
133
+ if source_data:
134
+ config_data.update(source_data)
135
+ except Exception as e:
136
+ log.warning(
137
+ "Failed to load config from source",
138
+ source=entry.name,
139
+ error=str(e)
140
+ )
141
+
142
+ # Create config instance
143
+ return config_class.from_dict(config_data)
144
+
145
+
146
+ __all__ = [
147
+ "resolve_config_value",
148
+ "get_config_chain",
149
+ "load_all_configs",
150
+ "load_config_from_registry",
151
+ ]