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.
- provide/foundation/__init__.py +36 -10
- provide/foundation/archive/__init__.py +1 -1
- provide/foundation/archive/base.py +15 -14
- provide/foundation/archive/bzip2.py +40 -40
- provide/foundation/archive/gzip.py +42 -42
- provide/foundation/archive/operations.py +93 -96
- provide/foundation/archive/tar.py +33 -31
- provide/foundation/archive/zip.py +52 -50
- provide/foundation/asynctools/__init__.py +20 -0
- provide/foundation/asynctools/core.py +126 -0
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +15 -9
- provide/foundation/cli/commands/logs/__init__.py +3 -3
- provide/foundation/cli/commands/logs/generate.py +2 -2
- provide/foundation/cli/commands/logs/query.py +4 -4
- provide/foundation/cli/commands/logs/send.py +3 -3
- provide/foundation/cli/commands/logs/tail.py +3 -3
- provide/foundation/cli/decorators.py +11 -11
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -40
- provide/foundation/cli/utils.py +21 -18
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +477 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +6 -20
- provide/foundation/config/loader.py +10 -4
- provide/foundation/config/sync.py +8 -6
- provide/foundation/config/types.py +5 -5
- provide/foundation/config/validators.py +4 -4
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +36 -14
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +88 -110
- provide/foundation/crypto/certificates/__init__.py +9 -5
- provide/foundation/crypto/certificates/base.py +2 -2
- provide/foundation/crypto/certificates/certificate.py +48 -19
- provide/foundation/crypto/certificates/factory.py +26 -18
- provide/foundation/crypto/certificates/generator.py +24 -23
- provide/foundation/crypto/certificates/loader.py +24 -16
- provide/foundation/crypto/certificates/operations.py +17 -10
- provide/foundation/crypto/certificates/trust.py +21 -21
- provide/foundation/env/__init__.py +28 -0
- provide/foundation/env/core.py +218 -0
- provide/foundation/errors/__init__.py +3 -3
- provide/foundation/errors/decorators.py +0 -234
- provide/foundation/errors/types.py +0 -98
- provide/foundation/eventsets/display.py +13 -14
- provide/foundation/eventsets/registry.py +61 -31
- provide/foundation/eventsets/resolver.py +50 -46
- provide/foundation/eventsets/sets/das.py +8 -8
- provide/foundation/eventsets/sets/database.py +14 -14
- provide/foundation/eventsets/sets/http.py +21 -21
- provide/foundation/eventsets/sets/llm.py +16 -16
- provide/foundation/eventsets/sets/task_queue.py +13 -13
- provide/foundation/eventsets/types.py +7 -7
- provide/foundation/file/directory.py +14 -23
- provide/foundation/file/lock.py +4 -3
- provide/foundation/hub/components.py +75 -389
- provide/foundation/hub/config.py +157 -0
- provide/foundation/hub/discovery.py +63 -0
- provide/foundation/hub/handlers.py +89 -0
- provide/foundation/hub/lifecycle.py +195 -0
- provide/foundation/hub/manager.py +7 -4
- provide/foundation/hub/processors.py +49 -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 +14 -14
- provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
- provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
- provide/foundation/logger/__init__.py +0 -1
- provide/foundation/logger/config/base.py +1 -1
- provide/foundation/logger/config/logging.py +69 -299
- provide/foundation/logger/config/telemetry.py +39 -121
- provide/foundation/logger/factories.py +2 -2
- provide/foundation/logger/processors/main.py +12 -10
- provide/foundation/logger/ratelimit/limiters.py +4 -4
- provide/foundation/logger/ratelimit/processor.py +1 -1
- provide/foundation/logger/setup/coordinator.py +39 -25
- provide/foundation/logger/setup/processors.py +3 -3
- provide/foundation/logger/setup/testing.py +14 -0
- provide/foundation/logger/trace.py +5 -5
- provide/foundation/metrics/__init__.py +1 -1
- provide/foundation/metrics/otel.py +3 -1
- provide/foundation/observability/__init__.py +3 -3
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +48 -0
- provide/foundation/process/lifecycle.py +69 -46
- provide/foundation/resilience/__init__.py +36 -0
- provide/foundation/resilience/circuit.py +166 -0
- provide/foundation/resilience/decorators.py +236 -0
- provide/foundation/resilience/fallback.py +208 -0
- provide/foundation/resilience/retry.py +327 -0
- provide/foundation/serialization/__init__.py +16 -0
- provide/foundation/serialization/core.py +70 -0
- provide/foundation/streams/config.py +78 -0
- provide/foundation/streams/console.py +4 -5
- provide/foundation/streams/core.py +5 -2
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +29 -9
- provide/foundation/testing/archive/__init__.py +7 -7
- provide/foundation/testing/archive/fixtures.py +58 -54
- provide/foundation/testing/cli.py +30 -20
- provide/foundation/testing/common/__init__.py +13 -15
- provide/foundation/testing/common/fixtures.py +27 -57
- provide/foundation/testing/file/__init__.py +15 -15
- provide/foundation/testing/file/content_fixtures.py +289 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +42 -516
- provide/foundation/testing/file/special_fixtures.py +145 -0
- provide/foundation/testing/logger.py +89 -8
- provide/foundation/testing/mocking/__init__.py +21 -21
- provide/foundation/testing/mocking/fixtures.py +80 -67
- provide/foundation/testing/process/__init__.py +23 -23
- provide/foundation/testing/process/async_fixtures.py +414 -0
- provide/foundation/testing/process/fixtures.py +48 -571
- provide/foundation/testing/process/subprocess_fixtures.py +210 -0
- provide/foundation/testing/threading/__init__.py +17 -17
- provide/foundation/testing/threading/basic_fixtures.py +105 -0
- provide/foundation/testing/threading/data_fixtures.py +101 -0
- provide/foundation/testing/threading/execution_fixtures.py +278 -0
- provide/foundation/testing/threading/fixtures.py +32 -502
- provide/foundation/testing/threading/sync_fixtures.py +100 -0
- provide/foundation/testing/time/__init__.py +11 -11
- provide/foundation/testing/time/fixtures.py +95 -83
- provide/foundation/testing/transport/__init__.py +9 -9
- provide/foundation/testing/transport/fixtures.py +54 -54
- provide/foundation/time/__init__.py +18 -0
- provide/foundation/time/core.py +63 -0
- provide/foundation/tools/__init__.py +2 -2
- provide/foundation/tools/base.py +68 -67
- provide/foundation/tools/cache.py +69 -74
- provide/foundation/tools/downloader.py +68 -62
- provide/foundation/tools/installer.py +51 -57
- provide/foundation/tools/registry.py +38 -45
- provide/foundation/tools/resolver.py +70 -68
- provide/foundation/tools/verifier.py +39 -50
- provide/foundation/tracer/spans.py +2 -14
- provide/foundation/transport/__init__.py +26 -33
- provide/foundation/transport/base.py +32 -30
- provide/foundation/transport/client.py +44 -49
- provide/foundation/transport/config.py +36 -107
- provide/foundation/transport/errors.py +13 -27
- provide/foundation/transport/http.py +69 -55
- provide/foundation/transport/middleware.py +113 -114
- provide/foundation/transport/registry.py +29 -27
- provide/foundation/transport/types.py +6 -6
- provide/foundation/utils/deps.py +17 -14
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
- provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
- provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
- /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
- /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
"""
|
2
|
+
Hub component discovery and dependency resolution utilities.
|
3
|
+
|
4
|
+
Provides functions for discovering components and resolving their dependencies
|
5
|
+
in the Hub registry system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any
|
9
|
+
|
10
|
+
from provide.foundation.hub.registry import Registry
|
11
|
+
|
12
|
+
|
13
|
+
def _get_registry_and_lock():
|
14
|
+
"""Get registry and lock from components module."""
|
15
|
+
from provide.foundation.hub.components import _registry_lock, get_component_registry
|
16
|
+
|
17
|
+
return get_component_registry(), _registry_lock
|
18
|
+
|
19
|
+
|
20
|
+
def resolve_component_dependencies(name: str, dimension: str) -> dict[str, Any]:
|
21
|
+
"""Resolve component dependencies recursively."""
|
22
|
+
registry, registry_lock = _get_registry_and_lock()
|
23
|
+
|
24
|
+
with registry_lock:
|
25
|
+
entry = registry.get_entry(name, dimension)
|
26
|
+
|
27
|
+
if not entry:
|
28
|
+
return {}
|
29
|
+
|
30
|
+
dependencies = {}
|
31
|
+
dep_names = entry.metadata.get("dependencies", [])
|
32
|
+
|
33
|
+
for dep_name in dep_names:
|
34
|
+
# Try same dimension first
|
35
|
+
dep_component = registry.get(dep_name, dimension)
|
36
|
+
if dep_component is not None:
|
37
|
+
dependencies[dep_name] = dep_component
|
38
|
+
else:
|
39
|
+
# Search across dimensions
|
40
|
+
dep_component = registry.get(dep_name)
|
41
|
+
if dep_component is not None:
|
42
|
+
dependencies[dep_name] = dep_component
|
43
|
+
|
44
|
+
return dependencies
|
45
|
+
|
46
|
+
|
47
|
+
def discover_components(
|
48
|
+
group: str,
|
49
|
+
dimension: str = "component",
|
50
|
+
registry: Registry | None = None,
|
51
|
+
) -> dict[str, type[Any]]:
|
52
|
+
"""
|
53
|
+
Discover and register components from entry points.
|
54
|
+
|
55
|
+
This is a stub for the TDD implementation.
|
56
|
+
"""
|
57
|
+
return {}
|
58
|
+
|
59
|
+
|
60
|
+
__all__ = [
|
61
|
+
"discover_components",
|
62
|
+
"resolve_component_dependencies",
|
63
|
+
]
|
@@ -0,0 +1,89 @@
|
|
1
|
+
"""
|
2
|
+
Hub error handler management utilities.
|
3
|
+
|
4
|
+
Provides functions for discovering and executing error handlers from the registry.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
from provide.foundation.errors.decorators import with_error_handling
|
10
|
+
from provide.foundation.hub.registry import RegistryEntry
|
11
|
+
from provide.foundation.logger import get_logger
|
12
|
+
|
13
|
+
log = get_logger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def _get_registry_and_lock():
|
17
|
+
"""Get registry and lock from components module."""
|
18
|
+
from provide.foundation.hub.components import (
|
19
|
+
ComponentCategory,
|
20
|
+
_registry_lock,
|
21
|
+
get_component_registry,
|
22
|
+
)
|
23
|
+
|
24
|
+
return get_component_registry(), _registry_lock, ComponentCategory
|
25
|
+
|
26
|
+
|
27
|
+
def get_handlers_for_exception(exception: Exception) -> list[RegistryEntry]:
|
28
|
+
"""Get error handlers that can handle the given exception type."""
|
29
|
+
registry, registry_lock, ComponentCategory = _get_registry_and_lock()
|
30
|
+
|
31
|
+
with registry_lock:
|
32
|
+
# Get all error handlers
|
33
|
+
all_entries = list(registry)
|
34
|
+
handlers = [
|
35
|
+
entry
|
36
|
+
for entry in all_entries
|
37
|
+
if entry.dimension == ComponentCategory.ERROR_HANDLER.value
|
38
|
+
]
|
39
|
+
|
40
|
+
# Filter by exception type
|
41
|
+
exception_type_name = type(exception).__name__
|
42
|
+
matching_handlers = []
|
43
|
+
|
44
|
+
for entry in handlers:
|
45
|
+
exception_types = entry.metadata.get("exception_types", [])
|
46
|
+
if any(
|
47
|
+
exc_type in exception_type_name or exception_type_name in exc_type
|
48
|
+
for exc_type in exception_types
|
49
|
+
):
|
50
|
+
matching_handlers.append(entry)
|
51
|
+
|
52
|
+
# Sort by priority (highest first)
|
53
|
+
matching_handlers.sort(
|
54
|
+
key=lambda e: e.metadata.get("priority", 0), reverse=True
|
55
|
+
)
|
56
|
+
return matching_handlers
|
57
|
+
|
58
|
+
|
59
|
+
@with_error_handling(
|
60
|
+
fallback=None,
|
61
|
+
context_provider=lambda: {
|
62
|
+
"function": "execute_error_handlers",
|
63
|
+
"module": "hub.handlers",
|
64
|
+
},
|
65
|
+
)
|
66
|
+
def execute_error_handlers(
|
67
|
+
exception: Exception, context: dict[str, Any]
|
68
|
+
) -> dict[str, Any] | None:
|
69
|
+
"""Execute error handlers until one handles the exception."""
|
70
|
+
handlers = get_handlers_for_exception(exception)
|
71
|
+
|
72
|
+
for entry in handlers:
|
73
|
+
handler = entry.value
|
74
|
+
try:
|
75
|
+
result = handler(exception, context)
|
76
|
+
if result is not None:
|
77
|
+
return result
|
78
|
+
except Exception as handler_error:
|
79
|
+
log.error(
|
80
|
+
"Error handler failed", handler=entry.name, error=str(handler_error)
|
81
|
+
)
|
82
|
+
|
83
|
+
return None
|
84
|
+
|
85
|
+
|
86
|
+
__all__ = [
|
87
|
+
"execute_error_handlers",
|
88
|
+
"get_handlers_for_exception",
|
89
|
+
]
|
@@ -0,0 +1,195 @@
|
|
1
|
+
"""
|
2
|
+
Hub component lifecycle management utilities.
|
3
|
+
|
4
|
+
Provides functions for initializing, managing, and cleaning up components
|
5
|
+
registered in the Hub registry system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import inspect
|
10
|
+
from typing import Any
|
11
|
+
|
12
|
+
from provide.foundation.logger import get_logger
|
13
|
+
|
14
|
+
log = get_logger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
def _get_registry_and_globals():
|
18
|
+
"""Get registry, lock, and initialized components from components module."""
|
19
|
+
from provide.foundation.hub.components import (
|
20
|
+
_initialized_components,
|
21
|
+
_registry_lock,
|
22
|
+
get_component_registry,
|
23
|
+
)
|
24
|
+
|
25
|
+
return get_component_registry(), _registry_lock, _initialized_components
|
26
|
+
|
27
|
+
|
28
|
+
def get_or_initialize_component(name: str, dimension: str) -> Any:
|
29
|
+
"""Get component, initializing lazily if needed."""
|
30
|
+
registry, registry_lock, initialized_components = _get_registry_and_globals()
|
31
|
+
|
32
|
+
with registry_lock:
|
33
|
+
key = (name, dimension)
|
34
|
+
|
35
|
+
# Return already initialized component
|
36
|
+
if key in initialized_components:
|
37
|
+
return initialized_components[key]
|
38
|
+
|
39
|
+
entry = registry.get_entry(name, dimension)
|
40
|
+
|
41
|
+
if not entry:
|
42
|
+
return None
|
43
|
+
|
44
|
+
# If already initialized, return it
|
45
|
+
if entry.value is not None:
|
46
|
+
initialized_components[key] = entry.value
|
47
|
+
return entry.value
|
48
|
+
|
49
|
+
# Initialize lazily
|
50
|
+
if entry.metadata.get("lazy", False):
|
51
|
+
factory = entry.metadata.get("factory")
|
52
|
+
if factory:
|
53
|
+
try:
|
54
|
+
component = factory()
|
55
|
+
# Update registry with initialized component
|
56
|
+
registry.register(
|
57
|
+
name=name,
|
58
|
+
value=component,
|
59
|
+
dimension=dimension,
|
60
|
+
metadata=entry.metadata,
|
61
|
+
replace=True,
|
62
|
+
)
|
63
|
+
initialized_components[key] = component
|
64
|
+
return component
|
65
|
+
except Exception as e:
|
66
|
+
log.error(
|
67
|
+
"Component initialization failed",
|
68
|
+
component=name,
|
69
|
+
dimension=dimension,
|
70
|
+
error=str(e),
|
71
|
+
)
|
72
|
+
|
73
|
+
return entry.value
|
74
|
+
|
75
|
+
|
76
|
+
async def initialize_async_component(name: str, dimension: str) -> Any:
|
77
|
+
"""Initialize component asynchronously."""
|
78
|
+
registry, registry_lock, initialized_components = _get_registry_and_globals()
|
79
|
+
|
80
|
+
with registry_lock:
|
81
|
+
key = (name, dimension)
|
82
|
+
|
83
|
+
# Return already initialized component
|
84
|
+
if key in initialized_components:
|
85
|
+
return initialized_components[key]
|
86
|
+
|
87
|
+
entry = registry.get_entry(name, dimension)
|
88
|
+
|
89
|
+
if not entry:
|
90
|
+
return None
|
91
|
+
|
92
|
+
# Initialize with async factory
|
93
|
+
if entry.metadata.get("async", False):
|
94
|
+
factory = entry.metadata.get("factory")
|
95
|
+
if factory:
|
96
|
+
try:
|
97
|
+
if inspect.iscoroutinefunction(factory):
|
98
|
+
component = await factory()
|
99
|
+
else:
|
100
|
+
component = factory()
|
101
|
+
|
102
|
+
# Update registry
|
103
|
+
registry.register(
|
104
|
+
name=name,
|
105
|
+
value=component,
|
106
|
+
dimension=dimension,
|
107
|
+
metadata=entry.metadata,
|
108
|
+
replace=True,
|
109
|
+
)
|
110
|
+
initialized_components[key] = component
|
111
|
+
return component
|
112
|
+
except Exception as e:
|
113
|
+
log.error(
|
114
|
+
"Async component initialization failed",
|
115
|
+
component=name,
|
116
|
+
dimension=dimension,
|
117
|
+
error=str(e),
|
118
|
+
)
|
119
|
+
|
120
|
+
return entry.value
|
121
|
+
|
122
|
+
|
123
|
+
def cleanup_all_components(dimension: str | None = None) -> None:
|
124
|
+
"""Clean up all components in dimension."""
|
125
|
+
registry, registry_lock, _ = _get_registry_and_globals()
|
126
|
+
|
127
|
+
with registry_lock:
|
128
|
+
if dimension:
|
129
|
+
entries = [entry for entry in registry if entry.dimension == dimension]
|
130
|
+
else:
|
131
|
+
entries = list(registry)
|
132
|
+
|
133
|
+
for entry in entries:
|
134
|
+
if entry.metadata.get("supports_cleanup", False):
|
135
|
+
component = entry.value
|
136
|
+
if hasattr(component, "cleanup"):
|
137
|
+
try:
|
138
|
+
cleanup_func = component.cleanup
|
139
|
+
if inspect.iscoroutinefunction(cleanup_func):
|
140
|
+
# Run async cleanup
|
141
|
+
loop = None
|
142
|
+
try:
|
143
|
+
loop = asyncio.get_event_loop()
|
144
|
+
if loop.is_running():
|
145
|
+
# Create task if loop is running
|
146
|
+
loop.create_task(cleanup_func())
|
147
|
+
else:
|
148
|
+
loop.run_until_complete(cleanup_func())
|
149
|
+
except RuntimeError:
|
150
|
+
# Create new loop if none exists
|
151
|
+
loop = asyncio.new_event_loop()
|
152
|
+
loop.run_until_complete(cleanup_func())
|
153
|
+
loop.close()
|
154
|
+
else:
|
155
|
+
cleanup_func()
|
156
|
+
except Exception as e:
|
157
|
+
log.error(
|
158
|
+
"Component cleanup failed",
|
159
|
+
component=entry.name,
|
160
|
+
dimension=entry.dimension,
|
161
|
+
error=str(e),
|
162
|
+
)
|
163
|
+
|
164
|
+
|
165
|
+
async def initialize_all_async_components() -> None:
|
166
|
+
"""Initialize all async components in dependency order."""
|
167
|
+
registry, _, _ = _get_registry_and_globals()
|
168
|
+
|
169
|
+
# Get all async components
|
170
|
+
async_components = [
|
171
|
+
entry for entry in registry if entry.metadata.get("async", False)
|
172
|
+
]
|
173
|
+
|
174
|
+
# Sort by priority for initialization order
|
175
|
+
async_components.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
176
|
+
|
177
|
+
# Initialize each component
|
178
|
+
for entry in async_components:
|
179
|
+
try:
|
180
|
+
await initialize_async_component(entry.name, entry.dimension)
|
181
|
+
except Exception as e:
|
182
|
+
log.error(
|
183
|
+
"Failed to initialize async component",
|
184
|
+
component=entry.name,
|
185
|
+
dimension=entry.dimension,
|
186
|
+
error=str(e),
|
187
|
+
)
|
188
|
+
|
189
|
+
|
190
|
+
__all__ = [
|
191
|
+
"cleanup_all_components",
|
192
|
+
"get_or_initialize_component",
|
193
|
+
"initialize_all_async_components",
|
194
|
+
"initialize_async_component",
|
195
|
+
]
|
@@ -19,7 +19,7 @@ except ImportError:
|
|
19
19
|
click = None
|
20
20
|
_HAS_CLICK = False
|
21
21
|
|
22
|
-
from provide.foundation.context import
|
22
|
+
from provide.foundation.context import CLIContext
|
23
23
|
from provide.foundation.errors.config import ValidationError
|
24
24
|
from provide.foundation.errors.decorators import with_error_handling
|
25
25
|
from provide.foundation.errors.resources import AlreadyExistsError
|
@@ -60,7 +60,7 @@ class Hub:
|
|
60
60
|
|
61
61
|
def __init__(
|
62
62
|
self,
|
63
|
-
context:
|
63
|
+
context: CLIContext | None = None,
|
64
64
|
component_registry: Registry | None = None,
|
65
65
|
command_registry: Registry | None = None,
|
66
66
|
) -> None:
|
@@ -68,11 +68,11 @@ class Hub:
|
|
68
68
|
Initialize the hub.
|
69
69
|
|
70
70
|
Args:
|
71
|
-
context: Foundation
|
71
|
+
context: Foundation CLIContext for configuration
|
72
72
|
component_registry: Custom component registry
|
73
73
|
command_registry: Custom command registry
|
74
74
|
"""
|
75
|
-
self.context = context or
|
75
|
+
self.context = context or CLIContext()
|
76
76
|
self._component_registry = component_registry or get_component_registry()
|
77
77
|
self._command_registry = command_registry or get_command_registry()
|
78
78
|
self._cli_group: click.Group | None = None
|
@@ -433,6 +433,9 @@ def get_hub() -> Hub:
|
|
433
433
|
# Double-check after acquiring lock
|
434
434
|
if _global_hub is None:
|
435
435
|
_global_hub = Hub()
|
436
|
+
# Bootstrap foundation components now that hub is ready
|
437
|
+
from provide.foundation.hub.components import bootstrap_foundation
|
438
|
+
bootstrap_foundation()
|
436
439
|
|
437
440
|
return _global_hub
|
438
441
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
"""
|
2
|
+
Hub processor pipeline management utilities.
|
3
|
+
|
4
|
+
Provides functions for managing log processors and processing stages
|
5
|
+
in the Hub registry system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from provide.foundation.hub.registry import RegistryEntry
|
9
|
+
|
10
|
+
|
11
|
+
def _get_registry_and_lock():
|
12
|
+
"""Get registry and lock from components module."""
|
13
|
+
from provide.foundation.hub.components import (
|
14
|
+
ComponentCategory,
|
15
|
+
_registry_lock,
|
16
|
+
get_component_registry,
|
17
|
+
)
|
18
|
+
|
19
|
+
return get_component_registry(), _registry_lock, ComponentCategory
|
20
|
+
|
21
|
+
|
22
|
+
def get_processor_pipeline() -> list[RegistryEntry]:
|
23
|
+
"""Get log processors ordered by priority."""
|
24
|
+
registry, registry_lock, ComponentCategory = _get_registry_and_lock()
|
25
|
+
|
26
|
+
with registry_lock:
|
27
|
+
# Get all processors
|
28
|
+
all_entries = list(registry)
|
29
|
+
processors = [
|
30
|
+
entry
|
31
|
+
for entry in all_entries
|
32
|
+
if entry.dimension == ComponentCategory.PROCESSOR.value
|
33
|
+
]
|
34
|
+
|
35
|
+
# Sort by priority (highest first)
|
36
|
+
processors.sort(key=lambda e: e.metadata.get("priority", 0), reverse=True)
|
37
|
+
return processors
|
38
|
+
|
39
|
+
|
40
|
+
def get_processors_for_stage(stage: str) -> list[RegistryEntry]:
|
41
|
+
"""Get processors for a specific processing stage."""
|
42
|
+
pipeline = get_processor_pipeline()
|
43
|
+
return [entry for entry in pipeline if entry.metadata.get("stage") == stage]
|
44
|
+
|
45
|
+
|
46
|
+
__all__ = [
|
47
|
+
"get_processor_pipeline",
|
48
|
+
"get_processors_for_stage",
|
49
|
+
]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""
|
2
|
+
Optional integrations for Foundation.
|
3
|
+
|
4
|
+
This module contains integrations with external services and tools
|
5
|
+
that are not part of the core foundation library.
|
6
|
+
"""
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
# Available integrations (import on demand)
|
10
|
+
# "openobserve",
|
11
|
+
]
|
@@ -1,11 +1,12 @@
|
|
1
1
|
"""
|
2
2
|
OpenObserve integration for Foundation.
|
3
3
|
|
4
|
-
Provides log querying and streaming capabilities
|
4
|
+
Provides log querying and streaming capabilities as an optional integration.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from provide.foundation.
|
8
|
-
from provide.foundation.
|
7
|
+
from provide.foundation.integrations.openobserve.client import OpenObserveClient
|
8
|
+
from provide.foundation.integrations.openobserve.config import OpenObserveConfig
|
9
|
+
from provide.foundation.integrations.openobserve.exceptions import (
|
9
10
|
OpenObserveAuthenticationError,
|
10
11
|
OpenObserveConfigError,
|
11
12
|
OpenObserveConnectionError,
|
@@ -13,7 +14,7 @@ from provide.foundation.observability.openobserve.exceptions import (
|
|
13
14
|
OpenObserveQueryError,
|
14
15
|
OpenObserveStreamingError,
|
15
16
|
)
|
16
|
-
from provide.foundation.
|
17
|
+
from provide.foundation.integrations.openobserve.formatters import (
|
17
18
|
format_csv,
|
18
19
|
format_json,
|
19
20
|
format_log_line,
|
@@ -21,13 +22,13 @@ from provide.foundation.observability.openobserve.formatters import (
|
|
21
22
|
format_summary,
|
22
23
|
format_table,
|
23
24
|
)
|
24
|
-
from provide.foundation.
|
25
|
+
from provide.foundation.integrations.openobserve.models import (
|
25
26
|
SearchQuery,
|
26
27
|
SearchResponse,
|
27
28
|
StreamInfo,
|
28
29
|
parse_relative_time,
|
29
30
|
)
|
30
|
-
from provide.foundation.
|
31
|
+
from provide.foundation.integrations.openobserve.search import (
|
31
32
|
aggregate_by_level,
|
32
33
|
get_current_trace_logs,
|
33
34
|
search_by_level,
|
@@ -36,13 +37,15 @@ from provide.foundation.observability.openobserve.search import (
|
|
36
37
|
search_errors,
|
37
38
|
search_logs,
|
38
39
|
)
|
39
|
-
from provide.foundation.
|
40
|
+
from provide.foundation.integrations.openobserve.streaming import (
|
40
41
|
stream_logs,
|
41
42
|
stream_search_http2,
|
42
43
|
tail_logs,
|
43
44
|
)
|
44
45
|
|
45
46
|
__all__ = [
|
47
|
+
# Configuration
|
48
|
+
"OpenObserveConfig",
|
46
49
|
# Client
|
47
50
|
"OpenObserveClient",
|
48
51
|
# Search functions
|
@@ -11,22 +11,22 @@ import requests
|
|
11
11
|
from requests.adapters import HTTPAdapter
|
12
12
|
from urllib3.util.retry import Retry
|
13
13
|
|
14
|
-
from provide.foundation.
|
15
|
-
from provide.foundation.observability.openobserve.auth import (
|
14
|
+
from provide.foundation.integrations.openobserve.auth import (
|
16
15
|
get_auth_headers,
|
17
16
|
validate_credentials,
|
18
17
|
)
|
19
|
-
from provide.foundation.
|
18
|
+
from provide.foundation.integrations.openobserve.exceptions import (
|
20
19
|
OpenObserveConfigError,
|
21
20
|
OpenObserveConnectionError,
|
22
21
|
OpenObserveQueryError,
|
23
22
|
)
|
24
|
-
from provide.foundation.
|
23
|
+
from provide.foundation.integrations.openobserve.models import (
|
25
24
|
SearchQuery,
|
26
25
|
SearchResponse,
|
27
26
|
StreamInfo,
|
28
27
|
parse_relative_time,
|
29
28
|
)
|
29
|
+
from provide.foundation.logger import get_logger
|
30
30
|
|
31
31
|
log = get_logger(__name__)
|
32
32
|
|
@@ -42,7 +42,7 @@ class OpenObserveClient:
|
|
42
42
|
organization: str = "default",
|
43
43
|
timeout: int = 30,
|
44
44
|
max_retries: int = 3,
|
45
|
-
):
|
45
|
+
) -> None:
|
46
46
|
"""Initialize OpenObserve client.
|
47
47
|
|
48
48
|
Args:
|
@@ -74,7 +74,7 @@ class OpenObserveClient:
|
|
74
74
|
|
75
75
|
@classmethod
|
76
76
|
def from_config(cls) -> "OpenObserveClient":
|
77
|
-
"""Create client from
|
77
|
+
"""Create client from OpenObserveConfig.
|
78
78
|
|
79
79
|
Returns:
|
80
80
|
Configured OpenObserveClient instance
|
@@ -82,26 +82,26 @@ class OpenObserveClient:
|
|
82
82
|
Raises:
|
83
83
|
OpenObserveConfigError: If configuration is missing
|
84
84
|
"""
|
85
|
-
from provide.foundation.
|
85
|
+
from provide.foundation.integrations.openobserve.config import OpenObserveConfig
|
86
86
|
|
87
|
-
config =
|
87
|
+
config = OpenObserveConfig.from_env()
|
88
88
|
|
89
|
-
if not config.
|
89
|
+
if not config.url:
|
90
90
|
raise OpenObserveConfigError(
|
91
91
|
"OpenObserve URL not configured. Set OPENOBSERVE_URL environment variable."
|
92
92
|
)
|
93
93
|
|
94
|
-
if not config.
|
94
|
+
if not config.user or not config.password:
|
95
95
|
raise OpenObserveConfigError(
|
96
96
|
"OpenObserve credentials not configured. "
|
97
97
|
"Set OPENOBSERVE_USER and OPENOBSERVE_PASSWORD environment variables."
|
98
98
|
)
|
99
99
|
|
100
100
|
return cls(
|
101
|
-
url=config.
|
102
|
-
username=config.
|
103
|
-
password=config.
|
104
|
-
organization=config.
|
101
|
+
url=config.url,
|
102
|
+
username=config.user,
|
103
|
+
password=config.password,
|
104
|
+
organization=config.org or "default",
|
105
105
|
)
|
106
106
|
|
107
107
|
def _make_request(
|