provide-foundation 0.0.0.dev2__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 +20 -20
- 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 +90 -91
- 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 +4 -4
- provide/foundation/cli/commands/logs/__init__.py +2 -2
- provide/foundation/cli/commands/logs/generate.py +2 -2
- provide/foundation/cli/commands/logs/query.py +3 -3
- provide/foundation/cli/commands/logs/send.py +2 -2
- provide/foundation/cli/commands/logs/tail.py +2 -2
- provide/foundation/cli/decorators.py +0 -1
- provide/foundation/cli/testing.py +0 -5
- provide/foundation/cli/utils.py +1 -2
- provide/foundation/config/__init__.py +19 -19
- provide/foundation/config/base.py +2 -2
- provide/foundation/config/converters.py +81 -83
- provide/foundation/config/defaults.py +1 -1
- provide/foundation/config/env.py +2 -1
- provide/foundation/config/loader.py +1 -1
- provide/foundation/config/sync.py +8 -6
- provide/foundation/config/types.py +5 -5
- provide/foundation/config/validators.py +4 -4
- provide/foundation/console/output.py +7 -7
- provide/foundation/context/core.py +19 -17
- 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 -2
- provide/foundation/errors/decorators.py +0 -3
- provide/foundation/errors/types.py +0 -1
- 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 +1 -1
- provide/foundation/file/lock.py +2 -3
- provide/foundation/hub/components.py +19 -21
- provide/foundation/hub/config.py +25 -19
- provide/foundation/hub/discovery.py +5 -4
- provide/foundation/hub/handlers.py +13 -5
- provide/foundation/hub/lifecycle.py +10 -9
- provide/foundation/hub/manager.py +3 -0
- provide/foundation/hub/processors.py +8 -3
- provide/foundation/integrations/__init__.py +1 -1
- provide/foundation/integrations/openobserve/client.py +2 -2
- provide/foundation/integrations/openobserve/commands.py +9 -9
- provide/foundation/integrations/openobserve/config.py +2 -2
- provide/foundation/integrations/openobserve/otlp.py +2 -2
- provide/foundation/integrations/openobserve/search.py +1 -2
- provide/foundation/integrations/openobserve/streaming.py +1 -1
- provide/foundation/logger/__init__.py +0 -1
- provide/foundation/logger/config/base.py +1 -1
- provide/foundation/logger/config/logging.py +19 -19
- provide/foundation/logger/config/telemetry.py +11 -13
- 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 +38 -24
- 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 +1 -1
- provide/foundation/process/__init__.py +1 -1
- provide/foundation/process/exit.py +6 -5
- provide/foundation/process/lifecycle.py +41 -18
- provide/foundation/resilience/__init__.py +6 -5
- provide/foundation/resilience/circuit.py +32 -30
- provide/foundation/resilience/decorators.py +58 -42
- provide/foundation/resilience/fallback.py +55 -40
- provide/foundation/resilience/retry.py +67 -65
- provide/foundation/serialization/__init__.py +16 -0
- provide/foundation/serialization/core.py +70 -0
- provide/foundation/streams/config.py +8 -9
- provide/foundation/streams/console.py +3 -3
- provide/foundation/streams/core.py +2 -2
- provide/foundation/streams/file.py +1 -1
- provide/foundation/testing/__init__.py +22 -7
- provide/foundation/testing/archive/__init__.py +7 -7
- provide/foundation/testing/archive/fixtures.py +58 -54
- provide/foundation/testing/cli.py +3 -6
- provide/foundation/testing/common/__init__.py +13 -13
- provide/foundation/testing/common/fixtures.py +27 -30
- provide/foundation/testing/file/__init__.py +15 -15
- provide/foundation/testing/file/content_fixtures.py +65 -92
- provide/foundation/testing/file/directory_fixtures.py +19 -19
- provide/foundation/testing/file/fixtures.py +14 -17
- provide/foundation/testing/file/special_fixtures.py +34 -42
- provide/foundation/testing/logger.py +28 -23
- 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 +89 -80
- provide/foundation/testing/process/fixtures.py +11 -13
- provide/foundation/testing/process/subprocess_fixtures.py +41 -40
- provide/foundation/testing/threading/__init__.py +17 -17
- provide/foundation/testing/threading/basic_fixtures.py +21 -17
- provide/foundation/testing/threading/data_fixtures.py +18 -16
- provide/foundation/testing/threading/execution_fixtures.py +67 -52
- provide/foundation/testing/threading/fixtures.py +10 -14
- provide/foundation/testing/threading/sync_fixtures.py +21 -18
- provide/foundation/testing/time/__init__.py +11 -11
- provide/foundation/testing/time/fixtures.py +91 -79
- 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 +62 -69
- provide/foundation/tools/downloader.py +51 -56
- 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 +1 -13
- 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 +11 -13
- provide/foundation/transport/errors.py +13 -27
- provide/foundation/transport/http.py +69 -55
- provide/foundation/transport/middleware.py +86 -81
- provide/foundation/transport/registry.py +29 -27
- provide/foundation/transport/types.py +6 -6
- provide/foundation/utils/deps.py +3 -2
- provide/foundation/utils/parsing.py +7 -7
- {provide_foundation-0.0.0.dev2.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.dev2.dist-info/RECORD +0 -225
- {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -24,13 +24,12 @@ except ImportError:
|
|
24
24
|
_HAS_CRYPTO = False
|
25
25
|
|
26
26
|
from provide.foundation import logger
|
27
|
-
from provide.foundation.crypto.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
from provide.foundation.crypto.certificates.base import (
|
28
|
+
CertificateBase,
|
29
|
+
CertificateError,
|
30
|
+
KeyPair,
|
31
|
+
PublicKey,
|
32
32
|
)
|
33
|
-
from provide.foundation.crypto.certificates.base import CertificateBase, CertificateError, KeyPair, PublicKey
|
34
33
|
|
35
34
|
|
36
35
|
def create_x509_certificate(
|
@@ -46,8 +45,12 @@ def create_x509_certificate(
|
|
46
45
|
try:
|
47
46
|
logger.debug("📜📝🚀 create_x509_certificate: Building certificate")
|
48
47
|
|
49
|
-
actual_issuer_name =
|
50
|
-
|
48
|
+
actual_issuer_name = (
|
49
|
+
issuer_name_override if issuer_name_override else base.issuer
|
50
|
+
)
|
51
|
+
actual_signing_key = (
|
52
|
+
signing_key_override if signing_key_override else private_key
|
53
|
+
)
|
51
54
|
|
52
55
|
if not actual_signing_key:
|
53
56
|
raise CertificateError(
|
@@ -147,7 +150,11 @@ def create_x509_certificate(
|
|
147
150
|
raise CertificateError("Failed to create X.509 certificate object") from e
|
148
151
|
|
149
152
|
|
150
|
-
def validate_signature(
|
153
|
+
def validate_signature(
|
154
|
+
signed_cert_obj: "X509Certificate",
|
155
|
+
signing_cert_obj: "X509Certificate",
|
156
|
+
signing_public_key: "PublicKey",
|
157
|
+
) -> bool:
|
151
158
|
"""Internal helper: Validates signature and issuer/subject match."""
|
152
159
|
if signed_cert_obj.issuer != signing_cert_obj.subject:
|
153
160
|
logger.debug(
|
@@ -195,4 +202,4 @@ def validate_signature(signed_cert_obj: "X509Certificate", signing_cert_obj: "X5
|
|
195
202
|
|
196
203
|
except Exception as e:
|
197
204
|
logger.debug(f"📜🔍❌ Signature validation failed: {type(e).__name__}: {e}")
|
198
|
-
return False
|
205
|
+
return False
|
@@ -1,9 +1,14 @@
|
|
1
1
|
"""Certificate trust chain and verification utilities."""
|
2
2
|
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from provide.foundation.crypto.certificates.certificate import Certificate
|
7
|
+
|
3
8
|
try:
|
4
9
|
from cryptography import x509
|
5
10
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
6
|
-
|
11
|
+
|
7
12
|
_HAS_CRYPTO = True
|
8
13
|
except ImportError:
|
9
14
|
x509 = None
|
@@ -23,45 +28,41 @@ def verify_trust(
|
|
23
28
|
) -> bool:
|
24
29
|
"""
|
25
30
|
Verifies if the other_cert is trusted based on this certificate's trust chain.
|
26
|
-
|
31
|
+
|
27
32
|
Args:
|
28
33
|
cert: The certificate doing the verification
|
29
34
|
other_cert: The certificate to verify
|
30
35
|
trust_chain: List of trusted certificates
|
31
|
-
|
36
|
+
|
32
37
|
Returns:
|
33
38
|
True if the certificate is trusted, False otherwise
|
34
39
|
"""
|
35
40
|
if other_cert is None:
|
36
41
|
raise CertificateError("Cannot verify trust: other_cert is None")
|
37
|
-
|
42
|
+
|
38
43
|
logger.debug(
|
39
44
|
f"📜🔍🚀 Verifying trust for cert S/N {other_cert.serial_number} "
|
40
45
|
f"against chain of S/N {cert.serial_number}"
|
41
46
|
)
|
42
|
-
|
47
|
+
|
43
48
|
if not other_cert.is_valid:
|
44
|
-
logger.debug(
|
45
|
-
"📜🔍⚠️ Trust verification failed: Other certificate is not valid"
|
46
|
-
)
|
49
|
+
logger.debug("📜🔍⚠️ Trust verification failed: Other certificate is not valid")
|
47
50
|
return False
|
48
51
|
if not other_cert.public_key:
|
49
52
|
raise CertificateError(
|
50
53
|
"Cannot verify trust: Other certificate has no public key"
|
51
54
|
)
|
52
|
-
|
55
|
+
|
53
56
|
if cert == other_cert:
|
54
57
|
logger.debug(
|
55
58
|
"📜🔍✅ Trust verified: Certificates are identical (based on subject/serial)"
|
56
59
|
)
|
57
60
|
return True
|
58
|
-
|
61
|
+
|
59
62
|
if other_cert in trust_chain:
|
60
|
-
logger.debug(
|
61
|
-
"📜🔍✅ Trust verified: Other certificate found in trust chain"
|
62
|
-
)
|
63
|
+
logger.debug("📜🔍✅ Trust verified: Other certificate found in trust chain")
|
63
64
|
return True
|
64
|
-
|
65
|
+
|
65
66
|
for trusted_cert in trust_chain:
|
66
67
|
logger.debug(
|
67
68
|
f"📜🔍🔁 Checking signature against trusted cert S/N {trusted_cert.serial_number}"
|
@@ -74,7 +75,7 @@ def verify_trust(
|
|
74
75
|
f"{trusted_cert.serial_number}"
|
75
76
|
)
|
76
77
|
return True
|
77
|
-
|
78
|
+
|
78
79
|
logger.debug(
|
79
80
|
"📜🔍❌ Trust verification failed: Other certificate not identical, "
|
80
81
|
"not in chain, and not signed by any cert in chain"
|
@@ -83,16 +84,15 @@ def verify_trust(
|
|
83
84
|
|
84
85
|
|
85
86
|
def validate_signature_wrapper(
|
86
|
-
signed_cert: "Certificate",
|
87
|
-
signing_cert: "Certificate"
|
87
|
+
signed_cert: "Certificate", signing_cert: "Certificate"
|
88
88
|
) -> bool:
|
89
89
|
"""
|
90
90
|
Internal helper: Validates signature and issuer/subject match.
|
91
|
-
|
91
|
+
|
92
92
|
Args:
|
93
93
|
signed_cert: The certificate that was signed
|
94
94
|
signing_cert: The certificate that did the signing
|
95
|
-
|
95
|
+
|
96
96
|
Returns:
|
97
97
|
True if signature is valid, False otherwise
|
98
98
|
"""
|
@@ -101,7 +101,7 @@ def validate_signature_wrapper(
|
|
101
101
|
"📜🔍❌ Cannot validate signature: Certificate object(s) not initialized"
|
102
102
|
)
|
103
103
|
return False
|
104
|
-
|
104
|
+
|
105
105
|
return validate_signature(
|
106
106
|
signed_cert._cert, signing_cert._cert, signing_cert.public_key
|
107
|
-
)
|
107
|
+
)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""
|
2
|
+
Environment variable access utilities for Foundation.
|
3
|
+
|
4
|
+
Provides consistent environment variable handling with validation,
|
5
|
+
testing support, and integration with Foundation's configuration system.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from provide.foundation.env.core import (
|
9
|
+
get_env,
|
10
|
+
get_env_bool,
|
11
|
+
get_env_float,
|
12
|
+
get_env_int,
|
13
|
+
get_env_list,
|
14
|
+
has_env,
|
15
|
+
set_env,
|
16
|
+
unset_env,
|
17
|
+
)
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
"get_env",
|
21
|
+
"get_env_bool",
|
22
|
+
"get_env_float",
|
23
|
+
"get_env_int",
|
24
|
+
"get_env_list",
|
25
|
+
"has_env",
|
26
|
+
"set_env",
|
27
|
+
"unset_env",
|
28
|
+
]
|
@@ -0,0 +1,218 @@
|
|
1
|
+
"""Core environment variable utilities for Foundation."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
|
5
|
+
from provide.foundation.errors import ValidationError
|
6
|
+
|
7
|
+
|
8
|
+
def get_env(key: str, default: str | None = None) -> str | None:
|
9
|
+
"""
|
10
|
+
Get environment variable with Foundation tracking.
|
11
|
+
|
12
|
+
Args:
|
13
|
+
key: Environment variable name
|
14
|
+
default: Default value if variable not found
|
15
|
+
|
16
|
+
Returns:
|
17
|
+
Environment variable value or default
|
18
|
+
|
19
|
+
Example:
|
20
|
+
>>> get_env("HOME") # doctest: +SKIP
|
21
|
+
'/Users/username'
|
22
|
+
>>> get_env("NONEXISTENT", "fallback")
|
23
|
+
'fallback'
|
24
|
+
"""
|
25
|
+
return os.environ.get(key, default)
|
26
|
+
|
27
|
+
|
28
|
+
def set_env(key: str, value: str) -> None:
|
29
|
+
"""
|
30
|
+
Set environment variable with validation.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
key: Environment variable name
|
34
|
+
value: Value to set
|
35
|
+
|
36
|
+
Raises:
|
37
|
+
ValidationError: If key or value is invalid
|
38
|
+
|
39
|
+
Example:
|
40
|
+
>>> set_env("TEST_VAR", "test_value")
|
41
|
+
>>> get_env("TEST_VAR")
|
42
|
+
'test_value'
|
43
|
+
"""
|
44
|
+
if not isinstance(key, str) or not key:
|
45
|
+
raise ValidationError("Environment variable key must be a non-empty string")
|
46
|
+
if not isinstance(value, str):
|
47
|
+
raise ValidationError("Environment variable value must be a string")
|
48
|
+
|
49
|
+
os.environ[key] = value
|
50
|
+
|
51
|
+
|
52
|
+
def unset_env(key: str) -> None:
|
53
|
+
"""
|
54
|
+
Remove environment variable if it exists.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
key: Environment variable name to remove
|
58
|
+
|
59
|
+
Example:
|
60
|
+
>>> set_env("TEMP_VAR", "value")
|
61
|
+
>>> unset_env("TEMP_VAR")
|
62
|
+
>>> has_env("TEMP_VAR")
|
63
|
+
False
|
64
|
+
"""
|
65
|
+
os.environ.pop(key, None)
|
66
|
+
|
67
|
+
|
68
|
+
def has_env(key: str) -> bool:
|
69
|
+
"""
|
70
|
+
Check if environment variable exists.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
key: Environment variable name
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
True if variable exists, False otherwise
|
77
|
+
|
78
|
+
Example:
|
79
|
+
>>> has_env("PATH")
|
80
|
+
True
|
81
|
+
>>> has_env("DEFINITELY_NOT_SET")
|
82
|
+
False
|
83
|
+
"""
|
84
|
+
return key in os.environ
|
85
|
+
|
86
|
+
|
87
|
+
def get_env_int(key: str, default: int | None = None) -> int | None:
|
88
|
+
"""
|
89
|
+
Get environment variable as integer.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
key: Environment variable name
|
93
|
+
default: Default value if variable not found or invalid
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
Integer value or default
|
97
|
+
|
98
|
+
Raises:
|
99
|
+
ValidationError: If value exists but cannot be converted to int
|
100
|
+
|
101
|
+
Example:
|
102
|
+
>>> set_env("PORT", "8080")
|
103
|
+
>>> get_env_int("PORT")
|
104
|
+
8080
|
105
|
+
>>> get_env_int("MISSING_PORT", 3000)
|
106
|
+
3000
|
107
|
+
"""
|
108
|
+
value = os.environ.get(key)
|
109
|
+
if value is None:
|
110
|
+
return default
|
111
|
+
|
112
|
+
try:
|
113
|
+
return int(value)
|
114
|
+
except ValueError as e:
|
115
|
+
raise ValidationError(
|
116
|
+
f"Environment variable {key}='{value}' cannot be converted to int"
|
117
|
+
) from e
|
118
|
+
|
119
|
+
|
120
|
+
def get_env_bool(key: str, default: bool | None = None) -> bool | None:
|
121
|
+
"""
|
122
|
+
Get environment variable as boolean.
|
123
|
+
|
124
|
+
Recognizes: true/false, yes/no, 1/0, on/off (case insensitive)
|
125
|
+
|
126
|
+
Args:
|
127
|
+
key: Environment variable name
|
128
|
+
default: Default value if variable not found
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
Boolean value or default
|
132
|
+
|
133
|
+
Raises:
|
134
|
+
ValidationError: If value exists but cannot be converted to bool
|
135
|
+
|
136
|
+
Example:
|
137
|
+
>>> set_env("DEBUG", "true")
|
138
|
+
>>> get_env_bool("DEBUG")
|
139
|
+
True
|
140
|
+
>>> set_env("VERBOSE", "no")
|
141
|
+
>>> get_env_bool("VERBOSE")
|
142
|
+
False
|
143
|
+
"""
|
144
|
+
value = os.environ.get(key)
|
145
|
+
if value is None:
|
146
|
+
return default
|
147
|
+
|
148
|
+
value_lower = value.lower().strip()
|
149
|
+
if value_lower in ("true", "yes", "1", "on"):
|
150
|
+
return True
|
151
|
+
elif value_lower in ("false", "no", "0", "off"):
|
152
|
+
return False
|
153
|
+
else:
|
154
|
+
raise ValidationError(
|
155
|
+
f"Environment variable {key}='{value}' cannot be converted to bool"
|
156
|
+
)
|
157
|
+
|
158
|
+
|
159
|
+
def get_env_float(key: str, default: float | None = None) -> float | None:
|
160
|
+
"""
|
161
|
+
Get environment variable as float.
|
162
|
+
|
163
|
+
Args:
|
164
|
+
key: Environment variable name
|
165
|
+
default: Default value if variable not found or invalid
|
166
|
+
|
167
|
+
Returns:
|
168
|
+
Float value or default
|
169
|
+
|
170
|
+
Raises:
|
171
|
+
ValidationError: If value exists but cannot be converted to float
|
172
|
+
|
173
|
+
Example:
|
174
|
+
>>> set_env("TIMEOUT", "30.5")
|
175
|
+
>>> get_env_float("TIMEOUT")
|
176
|
+
30.5
|
177
|
+
"""
|
178
|
+
value = os.environ.get(key)
|
179
|
+
if value is None:
|
180
|
+
return default
|
181
|
+
|
182
|
+
try:
|
183
|
+
return float(value)
|
184
|
+
except ValueError as e:
|
185
|
+
raise ValidationError(
|
186
|
+
f"Environment variable {key}='{value}' cannot be converted to float"
|
187
|
+
) from e
|
188
|
+
|
189
|
+
|
190
|
+
def get_env_list(
|
191
|
+
key: str, separator: str = ",", default: list[str] | None = None
|
192
|
+
) -> list[str] | None:
|
193
|
+
"""
|
194
|
+
Get environment variable as list of strings.
|
195
|
+
|
196
|
+
Args:
|
197
|
+
key: Environment variable name
|
198
|
+
separator: Character to split on (default: comma)
|
199
|
+
default: Default value if variable not found
|
200
|
+
|
201
|
+
Returns:
|
202
|
+
List of strings or default
|
203
|
+
|
204
|
+
Example:
|
205
|
+
>>> set_env("ALLOWED_HOSTS", "localhost,127.0.0.1,example.com")
|
206
|
+
>>> get_env_list("ALLOWED_HOSTS")
|
207
|
+
['localhost', '127.0.0.1', 'example.com']
|
208
|
+
>>> get_env_list("MISSING", default=["fallback"])
|
209
|
+
['fallback']
|
210
|
+
"""
|
211
|
+
value = os.environ.get(key)
|
212
|
+
if value is None:
|
213
|
+
return default
|
214
|
+
|
215
|
+
if not value.strip():
|
216
|
+
return []
|
217
|
+
|
218
|
+
return [item.strip() for item in value.split(separator) if item.strip()]
|
@@ -6,8 +6,6 @@ and utilities for robust error handling throughout the application.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
from provide.foundation.errors.auth import AuthenticationError, AuthorizationError
|
9
|
-
# Re-export from resilience module for compatibility
|
10
|
-
from provide.foundation.resilience.decorators import retry as retry_on_error
|
11
9
|
from provide.foundation.errors.base import FoundationError
|
12
10
|
from provide.foundation.errors.config import (
|
13
11
|
ConfigurationError,
|
@@ -53,6 +51,9 @@ from provide.foundation.errors.types import (
|
|
53
51
|
ErrorMetadata,
|
54
52
|
)
|
55
53
|
|
54
|
+
# Re-export from resilience module for compatibility
|
55
|
+
from provide.foundation.resilience.decorators import retry as retry_on_error
|
56
|
+
|
56
57
|
__all__ = [
|
57
58
|
"AlreadyExistsError",
|
58
59
|
"AuthenticationError",
|
@@ -144,7 +144,6 @@ def with_error_handling(
|
|
144
144
|
return decorator(func)
|
145
145
|
|
146
146
|
|
147
|
-
|
148
147
|
def suppress_and_log(
|
149
148
|
*exceptions: type[Exception],
|
150
149
|
fallback: Any = None,
|
@@ -249,5 +248,3 @@ def fallback_on_error(
|
|
249
248
|
return wrapper # type: ignore
|
250
249
|
|
251
250
|
return decorator
|
252
|
-
|
253
|
-
|
@@ -2,10 +2,9 @@
|
|
2
2
|
Event set display utilities for Foundation.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from provide.foundation.
|
6
|
-
|
7
|
-
from provide.foundation.eventsets.registry import get_registry, discover_event_sets
|
5
|
+
from provide.foundation.eventsets.registry import discover_event_sets, get_registry
|
8
6
|
from provide.foundation.eventsets.resolver import get_resolver
|
7
|
+
from provide.foundation.logger import get_logger
|
9
8
|
|
10
9
|
logger = get_logger(__name__)
|
11
10
|
|
@@ -17,16 +16,16 @@ def show_event_matrix() -> None:
|
|
17
16
|
"""
|
18
17
|
# Ensure event sets are discovered
|
19
18
|
discover_event_sets()
|
20
|
-
|
19
|
+
|
21
20
|
registry = get_registry()
|
22
21
|
resolver = get_resolver()
|
23
|
-
|
22
|
+
|
24
23
|
# Force resolution to ensure everything is loaded
|
25
24
|
resolver.resolve()
|
26
|
-
|
25
|
+
|
27
26
|
lines: list[str] = ["Foundation Event Sets: Active Configuration"]
|
28
27
|
lines.append("=" * 70)
|
29
|
-
|
28
|
+
|
30
29
|
# Show registered event sets
|
31
30
|
event_sets = registry.list_event_sets()
|
32
31
|
if event_sets:
|
@@ -35,7 +34,7 @@ def show_event_matrix() -> None:
|
|
35
34
|
lines.append(f"\n {config.name} (priority: {config.priority})")
|
36
35
|
if config.description:
|
37
36
|
lines.append(f" {config.description}")
|
38
|
-
|
37
|
+
|
39
38
|
# Show field mappings
|
40
39
|
if config.field_mappings:
|
41
40
|
lines.append(f" Field Mappings ({len(config.field_mappings)}):")
|
@@ -43,7 +42,7 @@ def show_event_matrix() -> None:
|
|
43
42
|
lines.append(f" - {mapping.log_key}")
|
44
43
|
if len(config.field_mappings) > 5:
|
45
44
|
lines.append(f" ... and {len(config.field_mappings) - 5} more")
|
46
|
-
|
45
|
+
|
47
46
|
# Show event sets
|
48
47
|
if config.event_sets:
|
49
48
|
lines.append(f" Event Sets ({len(config.event_sets)}):")
|
@@ -59,15 +58,15 @@ def show_event_matrix() -> None:
|
|
59
58
|
)
|
60
59
|
else:
|
61
60
|
lines.append("\n (No event sets registered)")
|
62
|
-
|
61
|
+
|
63
62
|
lines.append("\n" + "=" * 70)
|
64
|
-
|
63
|
+
|
65
64
|
# Show resolved state
|
66
65
|
if resolver._resolved:
|
67
66
|
lines.append("\nResolver State:")
|
68
67
|
lines.append(f" Total Field Mappings: {len(resolver._field_mappings)}")
|
69
68
|
lines.append(f" Total Event Sets: {len(resolver._event_sets)}")
|
70
|
-
|
69
|
+
|
71
70
|
# Show sample visual markers
|
72
71
|
if resolver._event_sets:
|
73
72
|
lines.append("\n Sample Visual Markers:")
|
@@ -79,6 +78,6 @@ def show_event_matrix() -> None:
|
|
79
78
|
lines.append(f" {marker} -> {key}")
|
80
79
|
else:
|
81
80
|
lines.append("\n (Resolver not yet initialized)")
|
82
|
-
|
81
|
+
|
83
82
|
# Log the complete display
|
84
|
-
logger.info("\n".join(lines))
|
83
|
+
logger.info("\n".join(lines))
|