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.
Files changed (155) hide show
  1. provide/foundation/__init__.py +20 -20
  2. provide/foundation/archive/__init__.py +1 -1
  3. provide/foundation/archive/base.py +15 -14
  4. provide/foundation/archive/bzip2.py +40 -40
  5. provide/foundation/archive/gzip.py +42 -42
  6. provide/foundation/archive/operations.py +90 -91
  7. provide/foundation/archive/tar.py +33 -31
  8. provide/foundation/archive/zip.py +52 -50
  9. provide/foundation/asynctools/__init__.py +20 -0
  10. provide/foundation/asynctools/core.py +126 -0
  11. provide/foundation/cli/__init__.py +2 -2
  12. provide/foundation/cli/commands/deps.py +4 -4
  13. provide/foundation/cli/commands/logs/__init__.py +2 -2
  14. provide/foundation/cli/commands/logs/generate.py +2 -2
  15. provide/foundation/cli/commands/logs/query.py +3 -3
  16. provide/foundation/cli/commands/logs/send.py +2 -2
  17. provide/foundation/cli/commands/logs/tail.py +2 -2
  18. provide/foundation/cli/decorators.py +0 -1
  19. provide/foundation/cli/testing.py +0 -5
  20. provide/foundation/cli/utils.py +1 -2
  21. provide/foundation/config/__init__.py +19 -19
  22. provide/foundation/config/base.py +2 -2
  23. provide/foundation/config/converters.py +81 -83
  24. provide/foundation/config/defaults.py +1 -1
  25. provide/foundation/config/env.py +2 -1
  26. provide/foundation/config/loader.py +1 -1
  27. provide/foundation/config/sync.py +8 -6
  28. provide/foundation/config/types.py +5 -5
  29. provide/foundation/config/validators.py +4 -4
  30. provide/foundation/console/output.py +7 -7
  31. provide/foundation/context/core.py +19 -17
  32. provide/foundation/crypto/certificates/__init__.py +9 -5
  33. provide/foundation/crypto/certificates/base.py +2 -2
  34. provide/foundation/crypto/certificates/certificate.py +48 -19
  35. provide/foundation/crypto/certificates/factory.py +26 -18
  36. provide/foundation/crypto/certificates/generator.py +24 -23
  37. provide/foundation/crypto/certificates/loader.py +24 -16
  38. provide/foundation/crypto/certificates/operations.py +17 -10
  39. provide/foundation/crypto/certificates/trust.py +21 -21
  40. provide/foundation/env/__init__.py +28 -0
  41. provide/foundation/env/core.py +218 -0
  42. provide/foundation/errors/__init__.py +3 -2
  43. provide/foundation/errors/decorators.py +0 -3
  44. provide/foundation/errors/types.py +0 -1
  45. provide/foundation/eventsets/display.py +13 -14
  46. provide/foundation/eventsets/registry.py +61 -31
  47. provide/foundation/eventsets/resolver.py +50 -46
  48. provide/foundation/eventsets/sets/das.py +8 -8
  49. provide/foundation/eventsets/sets/database.py +14 -14
  50. provide/foundation/eventsets/sets/http.py +21 -21
  51. provide/foundation/eventsets/sets/llm.py +16 -16
  52. provide/foundation/eventsets/sets/task_queue.py +13 -13
  53. provide/foundation/eventsets/types.py +7 -7
  54. provide/foundation/file/directory.py +1 -1
  55. provide/foundation/file/lock.py +2 -3
  56. provide/foundation/hub/components.py +19 -21
  57. provide/foundation/hub/config.py +25 -19
  58. provide/foundation/hub/discovery.py +5 -4
  59. provide/foundation/hub/handlers.py +13 -5
  60. provide/foundation/hub/lifecycle.py +10 -9
  61. provide/foundation/hub/manager.py +3 -0
  62. provide/foundation/hub/processors.py +8 -3
  63. provide/foundation/integrations/__init__.py +1 -1
  64. provide/foundation/integrations/openobserve/client.py +2 -2
  65. provide/foundation/integrations/openobserve/commands.py +9 -9
  66. provide/foundation/integrations/openobserve/config.py +2 -2
  67. provide/foundation/integrations/openobserve/otlp.py +2 -2
  68. provide/foundation/integrations/openobserve/search.py +1 -2
  69. provide/foundation/integrations/openobserve/streaming.py +1 -1
  70. provide/foundation/logger/__init__.py +0 -1
  71. provide/foundation/logger/config/base.py +1 -1
  72. provide/foundation/logger/config/logging.py +19 -19
  73. provide/foundation/logger/config/telemetry.py +11 -13
  74. provide/foundation/logger/factories.py +2 -2
  75. provide/foundation/logger/processors/main.py +12 -10
  76. provide/foundation/logger/ratelimit/limiters.py +4 -4
  77. provide/foundation/logger/ratelimit/processor.py +1 -1
  78. provide/foundation/logger/setup/coordinator.py +38 -24
  79. provide/foundation/logger/setup/processors.py +3 -3
  80. provide/foundation/logger/setup/testing.py +14 -0
  81. provide/foundation/logger/trace.py +5 -5
  82. provide/foundation/metrics/__init__.py +1 -1
  83. provide/foundation/metrics/otel.py +3 -1
  84. provide/foundation/observability/__init__.py +1 -1
  85. provide/foundation/process/__init__.py +1 -1
  86. provide/foundation/process/exit.py +6 -5
  87. provide/foundation/process/lifecycle.py +41 -18
  88. provide/foundation/resilience/__init__.py +6 -5
  89. provide/foundation/resilience/circuit.py +32 -30
  90. provide/foundation/resilience/decorators.py +58 -42
  91. provide/foundation/resilience/fallback.py +55 -40
  92. provide/foundation/resilience/retry.py +67 -65
  93. provide/foundation/serialization/__init__.py +16 -0
  94. provide/foundation/serialization/core.py +70 -0
  95. provide/foundation/streams/config.py +8 -9
  96. provide/foundation/streams/console.py +3 -3
  97. provide/foundation/streams/core.py +2 -2
  98. provide/foundation/streams/file.py +1 -1
  99. provide/foundation/testing/__init__.py +22 -7
  100. provide/foundation/testing/archive/__init__.py +7 -7
  101. provide/foundation/testing/archive/fixtures.py +58 -54
  102. provide/foundation/testing/cli.py +3 -6
  103. provide/foundation/testing/common/__init__.py +13 -13
  104. provide/foundation/testing/common/fixtures.py +27 -30
  105. provide/foundation/testing/file/__init__.py +15 -15
  106. provide/foundation/testing/file/content_fixtures.py +65 -92
  107. provide/foundation/testing/file/directory_fixtures.py +19 -19
  108. provide/foundation/testing/file/fixtures.py +14 -17
  109. provide/foundation/testing/file/special_fixtures.py +34 -42
  110. provide/foundation/testing/logger.py +28 -23
  111. provide/foundation/testing/mocking/__init__.py +21 -21
  112. provide/foundation/testing/mocking/fixtures.py +80 -67
  113. provide/foundation/testing/process/__init__.py +23 -23
  114. provide/foundation/testing/process/async_fixtures.py +89 -80
  115. provide/foundation/testing/process/fixtures.py +11 -13
  116. provide/foundation/testing/process/subprocess_fixtures.py +41 -40
  117. provide/foundation/testing/threading/__init__.py +17 -17
  118. provide/foundation/testing/threading/basic_fixtures.py +21 -17
  119. provide/foundation/testing/threading/data_fixtures.py +18 -16
  120. provide/foundation/testing/threading/execution_fixtures.py +67 -52
  121. provide/foundation/testing/threading/fixtures.py +10 -14
  122. provide/foundation/testing/threading/sync_fixtures.py +21 -18
  123. provide/foundation/testing/time/__init__.py +11 -11
  124. provide/foundation/testing/time/fixtures.py +91 -79
  125. provide/foundation/testing/transport/__init__.py +9 -9
  126. provide/foundation/testing/transport/fixtures.py +54 -54
  127. provide/foundation/time/__init__.py +18 -0
  128. provide/foundation/time/core.py +63 -0
  129. provide/foundation/tools/__init__.py +2 -2
  130. provide/foundation/tools/base.py +68 -67
  131. provide/foundation/tools/cache.py +62 -69
  132. provide/foundation/tools/downloader.py +51 -56
  133. provide/foundation/tools/installer.py +51 -57
  134. provide/foundation/tools/registry.py +38 -45
  135. provide/foundation/tools/resolver.py +70 -68
  136. provide/foundation/tools/verifier.py +39 -50
  137. provide/foundation/tracer/spans.py +1 -13
  138. provide/foundation/transport/__init__.py +26 -33
  139. provide/foundation/transport/base.py +32 -30
  140. provide/foundation/transport/client.py +44 -49
  141. provide/foundation/transport/config.py +11 -13
  142. provide/foundation/transport/errors.py +13 -27
  143. provide/foundation/transport/http.py +69 -55
  144. provide/foundation/transport/middleware.py +86 -81
  145. provide/foundation/transport/registry.py +29 -27
  146. provide/foundation/transport/types.py +6 -6
  147. provide/foundation/utils/deps.py +3 -2
  148. provide/foundation/utils/parsing.py +7 -7
  149. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
  150. provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
  151. provide_foundation-0.0.0.dev2.dist-info/RECORD +0 -225
  152. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
  153. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
  154. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
  155. {provide_foundation-0.0.0.dev2.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ parse and validate environment variable values into the correct types.
8
8
  import json
9
9
  from typing import Any
10
10
 
11
- from provide.foundation.errors.decorators import with_error_handling
11
+ # Error handling decorator temporarily removed to break circular import
12
12
 
13
13
  # Type definitions to avoid circular imports
14
14
  LogLevelStr = str
@@ -32,13 +32,13 @@ _VALID_FORMATTER_TUPLE = (
32
32
  def parse_log_level(value: str) -> LogLevelStr:
33
33
  """
34
34
  Parse and validate log level string.
35
-
35
+
36
36
  Args:
37
37
  value: Log level string (case-insensitive)
38
-
38
+
39
39
  Returns:
40
40
  Valid log level string in uppercase
41
-
41
+
42
42
  Raises:
43
43
  ValueError: If the log level is invalid
44
44
  """
@@ -50,17 +50,16 @@ def parse_log_level(value: str) -> LogLevelStr:
50
50
  return level
51
51
 
52
52
 
53
-
54
53
  def parse_console_formatter(value: str) -> ConsoleFormatterStr:
55
54
  """
56
55
  Parse and validate console formatter string.
57
-
56
+
58
57
  Args:
59
58
  value: Formatter string (case-insensitive)
60
-
59
+
61
60
  Returns:
62
61
  Valid formatter string in lowercase
63
-
62
+
64
63
  Raises:
65
64
  ValueError: If the formatter is invalid
66
65
  """
@@ -72,21 +71,18 @@ def parse_console_formatter(value: str) -> ConsoleFormatterStr:
72
71
  return formatter
73
72
 
74
73
 
75
- @with_error_handling(
76
- fallback={},
77
- suppress=(ValueError, KeyError),
78
- context_provider=lambda: {"function": "parse_module_levels", "module": "config.converters"}
79
- )
74
+ # Temporarily remove error handling to break circular import
75
+ # @with_error_handling(...)
80
76
  def parse_module_levels(value: str | dict[str, str]) -> dict[str, LogLevelStr]:
81
77
  """
82
78
  Parse module-specific log levels from string format.
83
-
79
+
84
80
  Format: "module1:LEVEL,module2:LEVEL"
85
81
  Example: "auth.service:DEBUG,database:ERROR"
86
-
82
+
87
83
  Args:
88
84
  value: Comma-separated module:level pairs or dict
89
-
85
+
90
86
  Returns:
91
87
  Dictionary mapping module names to log levels
92
88
  """
@@ -100,64 +96,64 @@ def parse_module_levels(value: str | dict[str, str]) -> dict[str, LogLevelStr]:
100
96
  # Skip invalid levels silently
101
97
  continue
102
98
  return result
103
-
99
+
104
100
  if not value or not value.strip():
105
101
  return {}
106
-
102
+
107
103
  result = {}
108
104
  for pair in value.split(","):
109
105
  pair = pair.strip()
110
106
  if not pair:
111
107
  continue
112
-
108
+
113
109
  if ":" not in pair:
114
110
  # Skip invalid entries silently
115
111
  continue
116
-
112
+
117
113
  module, level = pair.split(":", 1)
118
114
  module = module.strip()
119
115
  level = level.strip()
120
-
116
+
121
117
  if module:
122
118
  try:
123
119
  result[module] = parse_log_level(level)
124
120
  except ValueError:
125
121
  # Skip invalid log levels silently
126
122
  continue
127
-
123
+
128
124
  return result
129
125
 
130
126
 
131
127
  def parse_rate_limits(value: str) -> dict[str, tuple[float, float]]:
132
128
  """
133
129
  Parse per-logger rate limits from string format.
134
-
130
+
135
131
  Format: "logger1:rate:capacity,logger2:rate:capacity"
136
132
  Example: "api:10.0:100.0,worker:5.0:50.0"
137
-
133
+
138
134
  Args:
139
135
  value: Comma-separated logger:rate:capacity triplets
140
-
136
+
141
137
  Returns:
142
138
  Dictionary mapping logger names to (rate, capacity) tuples
143
139
  """
144
140
  if not value or not value.strip():
145
141
  return {}
146
-
142
+
147
143
  result = {}
148
144
  for item in value.split(","):
149
145
  item = item.strip()
150
146
  if not item:
151
147
  continue
152
-
148
+
153
149
  parts = item.split(":")
154
150
  if len(parts) != 3:
155
151
  # Skip invalid entries silently
156
152
  continue
157
-
153
+
158
154
  logger, rate_str, capacity_str = parts
159
155
  logger = logger.strip()
160
-
156
+
161
157
  if logger:
162
158
  try:
163
159
  rate = float(rate_str.strip())
@@ -166,29 +162,29 @@ def parse_rate_limits(value: str) -> dict[str, tuple[float, float]]:
166
162
  except (ValueError, TypeError):
167
163
  # Skip invalid numbers silently
168
164
  continue
169
-
165
+
170
166
  return result
171
167
 
172
168
 
173
169
  def parse_foundation_log_output(value: str) -> str:
174
170
  """
175
171
  Parse and validate foundation log output destination.
176
-
172
+
177
173
  Args:
178
174
  value: Output destination string
179
-
175
+
180
176
  Returns:
181
177
  Valid output destination (stderr, stdout, main)
182
-
178
+
183
179
  Raises:
184
180
  ValueError: If the value is invalid
185
181
  """
186
182
  if not value:
187
183
  return "stderr"
188
-
184
+
189
185
  normalized = value.lower().strip()
190
186
  valid_options = ("stderr", "stdout", "main")
191
-
187
+
192
188
  if normalized in valid_options:
193
189
  return normalized
194
190
  else:
@@ -200,35 +196,35 @@ def parse_foundation_log_output(value: str) -> str:
200
196
  def parse_comma_list(value: str) -> list[str]:
201
197
  """
202
198
  Parse comma-separated list of strings.
203
-
199
+
204
200
  Args:
205
201
  value: Comma-separated string
206
-
202
+
207
203
  Returns:
208
204
  List of trimmed non-empty strings
209
205
  """
210
206
  if not value or not value.strip():
211
207
  return []
212
-
208
+
213
209
  return [item.strip() for item in value.split(",") if item.strip()]
214
210
 
215
211
 
216
212
  def parse_bool_extended(value: str | bool) -> bool:
217
213
  """
218
214
  Parse boolean from string with extended format support.
219
-
215
+
220
216
  Recognizes: true/false, yes/no, 1/0, on/off (case-insensitive)
221
-
217
+
222
218
  Args:
223
219
  value: Boolean string representation or bool
224
-
220
+
225
221
  Returns:
226
222
  Boolean value
227
223
  """
228
224
  # If already a bool, return as-is
229
225
  if isinstance(value, bool):
230
226
  return value
231
-
227
+
232
228
  # Convert to string and parse
233
229
  value_lower = str(value).lower().strip()
234
230
  return value_lower in ("true", "yes", "1", "on")
@@ -237,56 +233,58 @@ def parse_bool_extended(value: str | bool) -> bool:
237
233
  def parse_bool_strict(value: str | bool) -> bool:
238
234
  """
239
235
  Parse boolean from string with strict validation.
240
-
236
+
241
237
  Recognizes: true/false, yes/no, 1/0, on/off (case-insensitive)
242
-
238
+
243
239
  Args:
244
240
  value: Boolean string representation or bool
245
-
241
+
246
242
  Returns:
247
243
  Boolean value
248
-
244
+
249
245
  Raises:
250
246
  TypeError: If value is not a string or bool
251
247
  ValueError: If the value cannot be parsed as boolean
252
248
  """
253
249
  # Check type first
254
250
  if not isinstance(value, (str, bool)):
255
- raise TypeError(f"Boolean field requires str or bool, got {type(value).__name__}")
256
-
251
+ raise TypeError(
252
+ f"Boolean field requires str or bool, got {type(value).__name__}"
253
+ )
254
+
257
255
  # If already a bool, return as-is
258
256
  if isinstance(value, bool):
259
257
  return value
260
-
258
+
261
259
  # Convert to string and parse
262
260
  value_lower = value.lower().strip()
263
-
261
+
264
262
  if value_lower in ("true", "yes", "1", "on"):
265
263
  return True
266
264
  elif value_lower in ("false", "no", "0", "off"):
267
265
  return False
268
266
  else:
269
- raise ValueError(f"Invalid boolean value '{value}'. Valid options: true/false, yes/no, 1/0, on/off")
267
+ raise ValueError(
268
+ f"Invalid boolean value '{value}'. Valid options: true/false, yes/no, 1/0, on/off"
269
+ )
270
270
 
271
271
 
272
- @with_error_handling(
273
- fallback=0.0,
274
- context_provider=lambda: {"module": "config.converters"}
275
- )
272
+ # Temporarily remove error handling to break circular import
273
+ # @with_error_handling(...)
276
274
  def parse_float_with_validation(
277
275
  value: str, min_val: float | None = None, max_val: float | None = None
278
276
  ) -> float:
279
277
  """
280
278
  Parse float with optional range validation.
281
-
279
+
282
280
  Args:
283
281
  value: String representation of float
284
282
  min_val: Minimum allowed value (inclusive)
285
283
  max_val: Maximum allowed value (inclusive)
286
-
284
+
287
285
  Returns:
288
286
  Parsed float value
289
-
287
+
290
288
  Raises:
291
289
  ValueError: If value is not a valid float or out of range
292
290
  """
@@ -294,26 +292,26 @@ def parse_float_with_validation(
294
292
  result = float(value)
295
293
  except (ValueError, TypeError) as e:
296
294
  raise ValueError(f"Invalid float value '{value}': {e}")
297
-
295
+
298
296
  if min_val is not None and result < min_val:
299
297
  raise ValueError(f"Value {result} is below minimum {min_val}")
300
-
298
+
301
299
  if max_val is not None and result > max_val:
302
300
  raise ValueError(f"Value {result} is above maximum {max_val}")
303
-
301
+
304
302
  return result
305
303
 
306
304
 
307
305
  def parse_sample_rate(value: str) -> float:
308
306
  """
309
307
  Parse sampling rate (0.0 to 1.0).
310
-
308
+
311
309
  Args:
312
310
  value: String representation of sampling rate
313
-
311
+
314
312
  Returns:
315
313
  Float between 0.0 and 1.0
316
-
314
+
317
315
  Raises:
318
316
  ValueError: If value is not valid or out of range
319
317
  """
@@ -323,19 +321,19 @@ def parse_sample_rate(value: str) -> float:
323
321
  def parse_json_dict(value: str) -> dict[str, Any]:
324
322
  """
325
323
  Parse JSON string into dictionary.
326
-
324
+
327
325
  Args:
328
326
  value: JSON string
329
-
327
+
330
328
  Returns:
331
329
  Parsed dictionary
332
-
330
+
333
331
  Raises:
334
332
  ValueError: If JSON is invalid
335
333
  """
336
334
  if not value or not value.strip():
337
335
  return {}
338
-
336
+
339
337
  try:
340
338
  result = json.loads(value)
341
339
  if not isinstance(result, dict):
@@ -348,19 +346,19 @@ def parse_json_dict(value: str) -> dict[str, Any]:
348
346
  def parse_json_list(value: str) -> list[Any]:
349
347
  """
350
348
  Parse JSON string into list.
351
-
349
+
352
350
  Args:
353
351
  value: JSON string
354
-
352
+
355
353
  Returns:
356
354
  Parsed list
357
-
355
+
358
356
  Raises:
359
357
  ValueError: If JSON is invalid
360
358
  """
361
359
  if not value or not value.strip():
362
360
  return []
363
-
361
+
364
362
  try:
365
363
  result = json.loads(value)
366
364
  if not isinstance(result, list):
@@ -373,41 +371,42 @@ def parse_json_list(value: str) -> list[Any]:
373
371
  def parse_headers(value: str) -> dict[str, str]:
374
372
  """
375
373
  Parse HTTP headers from string format.
376
-
374
+
377
375
  Format: "key1=value1,key2=value2"
378
376
  Example: "Authorization=Bearer token,Content-Type=application/json"
379
-
377
+
380
378
  Args:
381
379
  value: Comma-separated key=value pairs
382
-
380
+
383
381
  Returns:
384
382
  Dictionary of headers
385
383
  """
386
384
  if not value or not value.strip():
387
385
  return {}
388
-
386
+
389
387
  result = {}
390
388
  for pair in value.split(","):
391
389
  pair = pair.strip()
392
390
  if not pair:
393
391
  continue
394
-
392
+
395
393
  if "=" not in pair:
396
394
  # Skip invalid entries
397
395
  continue
398
-
396
+
399
397
  key, val = pair.split("=", 1)
400
398
  key = key.strip()
401
399
  val = val.strip()
402
-
400
+
403
401
  if key:
404
402
  result[key] = val
405
-
403
+
406
404
  return result
407
405
 
408
406
 
409
407
  # Validators (used with validator parameter in field())
410
408
 
409
+
411
410
  def validate_log_level(instance: Any, attribute: Any, value: str) -> None:
412
411
  """Validate that a log level is valid."""
413
412
  if value not in _VALID_LOG_LEVEL_TUPLE:
@@ -455,11 +454,10 @@ def validate_overflow_policy(instance: Any, attribute: Any, value: str) -> None:
455
454
  )
456
455
 
457
456
 
458
-
459
457
  __all__ = [
460
458
  # Parsers/Converters
461
459
  "parse_log_level",
462
- "parse_console_formatter",
460
+ "parse_console_formatter",
463
461
  "parse_module_levels",
464
462
  "parse_rate_limits",
465
463
  "parse_comma_list",
@@ -476,4 +474,4 @@ __all__ = [
476
474
  "validate_positive",
477
475
  "validate_non_negative",
478
476
  "validate_overflow_policy",
479
- ]
477
+ ]
@@ -64,4 +64,4 @@ DEFAULT_TEST_CHECKPOINT_TIMEOUT = 5.0
64
64
  # =================================
65
65
  EXIT_SUCCESS = 0
66
66
  EXIT_ERROR = 1
67
- EXIT_SIGINT = 130 # Standard exit code for SIGINT
67
+ EXIT_SIGINT = 130 # Standard exit code for SIGINT
@@ -203,6 +203,7 @@ class RuntimeConfig(BaseConfig):
203
203
  else:
204
204
  # Try to infer parser from type
205
205
  from provide.foundation.utils.parsing import auto_parse
206
+
206
207
  value = auto_parse(attr, value)
207
208
 
208
209
  data[attr.name] = value
@@ -284,6 +285,7 @@ class RuntimeConfig(BaseConfig):
284
285
  else:
285
286
  # Try to infer parser from type
286
287
  from provide.foundation.utils.parsing import auto_parse
288
+
287
289
  value = auto_parse(attr, value)
288
290
 
289
291
  data[field_name] = value
@@ -306,7 +308,6 @@ class RuntimeConfig(BaseConfig):
306
308
  except Exception as e:
307
309
  raise ValueError(f"Failed to read secret from file '{file_path}': {e}")
308
310
 
309
-
310
311
  def to_env_dict(self, prefix: str = "", delimiter: str = "_") -> dict[str, str]:
311
312
  """
312
313
  Convert configuration to environment variable dictionary.
@@ -150,7 +150,7 @@ class FileConfigLoader(ConfigLoader):
150
150
  format=str(self.format),
151
151
  )
152
152
 
153
- def _ini_to_dict(self, parser) -> ConfigDict:
153
+ def _ini_to_dict(self, parser: object) -> ConfigDict:
154
154
  """Convert INI parser to dictionary."""
155
155
  result = {}
156
156
  for section in parser.sections():
@@ -14,18 +14,18 @@ from typing import Any, TypeVar
14
14
  from provide.foundation.config.base import BaseConfig
15
15
  from provide.foundation.config.env import RuntimeConfig
16
16
  from provide.foundation.config.loader import (
17
+ ConfigLoader,
17
18
  DictConfigLoader,
18
19
  FileConfigLoader,
19
20
  MultiSourceLoader,
20
21
  )
21
22
  from provide.foundation.config.manager import ConfigManager
22
23
  from provide.foundation.config.types import ConfigDict, ConfigSource
23
- from provide.foundation.config.loader import ConfigLoader
24
24
 
25
25
  T = TypeVar("T", bound=BaseConfig)
26
26
 
27
27
 
28
- def run_async(coro):
28
+ def run_async(coro: Any) -> Any:
29
29
  """
30
30
  Run an async coroutine in a sync context.
31
31
 
@@ -230,7 +230,7 @@ class SyncConfigManager:
230
230
 
231
231
  def __init__(self, loader: ConfigLoader | None = None) -> None:
232
232
  """Initialize sync config manager.
233
-
233
+
234
234
  Args:
235
235
  loader: Optional config loader for loading configurations.
236
236
  """
@@ -245,14 +245,16 @@ class SyncConfigManager:
245
245
  """Get a configuration by name (sync)."""
246
246
  return run_async(self._async_manager.get(name))
247
247
 
248
- def load(self, name: str, config_class: type[T], loader: ConfigLoader | None = None) -> T:
248
+ def load(
249
+ self, name: str, config_class: type[T], loader: ConfigLoader | None = None
250
+ ) -> T:
249
251
  """Load a configuration (sync).
250
-
252
+
251
253
  Args:
252
254
  name: Configuration name
253
255
  config_class: Configuration class
254
256
  loader: Optional loader (uses registered if None)
255
-
257
+
256
258
  Returns:
257
259
  Configuration instance
258
260
  """
@@ -20,31 +20,31 @@ class ConfigSource(Enum):
20
20
  ENV = 20
21
21
  RUNTIME = 30 # Highest precedence
22
22
 
23
- def __lt__(self, other):
23
+ def __lt__(self, other: object) -> bool:
24
24
  """Enable comparison for precedence."""
25
25
  if not isinstance(other, ConfigSource):
26
26
  return NotImplemented
27
27
  return self.value < other.value
28
28
 
29
- def __le__(self, other):
29
+ def __le__(self, other: object) -> bool:
30
30
  """Enable <= comparison for precedence."""
31
31
  if not isinstance(other, ConfigSource):
32
32
  return NotImplemented
33
33
  return self.value <= other.value
34
34
 
35
- def __gt__(self, other):
35
+ def __gt__(self, other: object) -> bool:
36
36
  """Enable > comparison for precedence."""
37
37
  if not isinstance(other, ConfigSource):
38
38
  return NotImplemented
39
39
  return self.value > other.value
40
40
 
41
- def __ge__(self, other):
41
+ def __ge__(self, other: object) -> bool:
42
42
  """Enable >= comparison for precedence."""
43
43
  if not isinstance(other, ConfigSource):
44
44
  return NotImplemented
45
45
  return self.value >= other.value
46
46
 
47
- def __eq__(self, other):
47
+ def __eq__(self, other: object) -> bool:
48
48
  """Enable == comparison for precedence."""
49
49
  if not isinstance(other, ConfigSource):
50
50
  return NotImplemented
@@ -22,7 +22,7 @@ def validate_choice(choices: list[Any]) -> Callable[[Any, Any, Any], None]:
22
22
  Validator function
23
23
  """
24
24
 
25
- def validator(instance, attribute, value):
25
+ def validator(instance: object, attribute: object, value: Any) -> None:
26
26
  if value not in choices:
27
27
  raise ValidationError(
28
28
  f"Invalid value '{value}' for {attribute.name}. "
@@ -46,7 +46,7 @@ def validate_range(
46
46
  Validator function
47
47
  """
48
48
 
49
- def validator(instance, attribute, value):
49
+ def validator(instance: object, attribute: object, value: Any) -> None:
50
50
  if not isinstance(value, (int, float)):
51
51
  raise ValidationError(f"Value must be a number, got {type(value).__name__}")
52
52
 
@@ -58,7 +58,7 @@ def validate_range(
58
58
  return validator
59
59
 
60
60
 
61
- def validate_positive(instance, attribute, value):
61
+ def validate_positive(instance: object, attribute: object, value: Any) -> None:
62
62
  """
63
63
  Validate that a numeric value is positive.
64
64
  """
@@ -69,7 +69,7 @@ def validate_positive(instance, attribute, value):
69
69
  raise ValidationError(f"Value must be positive, got {value}")
70
70
 
71
71
 
72
- def validate_non_negative(instance, attribute, value):
72
+ def validate_non_negative(instance: object, attribute: object, value: Any) -> None:
73
73
  """
74
74
  Validate that a numeric value is non-negative.
75
75
  """
@@ -42,18 +42,18 @@ def _should_use_json(ctx: CLIContext | None = None) -> bool:
42
42
  return ctx.json_output if ctx else False
43
43
 
44
44
 
45
- def _should_use_color(ctx: CLIContext | None = None, stream=None) -> bool:
45
+ def _should_use_color(ctx: CLIContext | None = None, stream: Any = None) -> bool:
46
46
  """Determine if color output should be used."""
47
47
  if ctx is None:
48
48
  ctx = _get_context()
49
49
 
50
50
  # Check FORCE_COLOR first (enables color even for non-TTY)
51
- force_color = os.environ.get('FORCE_COLOR', '').lower()
52
- if force_color in ('1', 'true', 'yes'):
51
+ force_color = os.environ.get("FORCE_COLOR", "").lower()
52
+ if force_color in ("1", "true", "yes"):
53
53
  return True
54
54
 
55
55
  # Check NO_COLOR (disables color even for TTY)
56
- if os.environ.get('NO_COLOR'):
56
+ if os.environ.get("NO_COLOR"):
57
57
  return False
58
58
 
59
59
  # Check context no_color setting
@@ -68,7 +68,7 @@ def _should_use_color(ctx: CLIContext | None = None, stream=None) -> bool:
68
68
 
69
69
 
70
70
  @with_error_handling(fallback=None, suppress=(TypeError, ValueError, AttributeError))
71
- def _output_json(data: Any, stream=sys.stdout) -> None:
71
+ def _output_json(data: Any, stream: Any = sys.stdout) -> None:
72
72
  """Output data as JSON."""
73
73
  json_str = json.dumps(data, indent=2, default=str)
74
74
  if _HAS_CLICK:
@@ -80,7 +80,7 @@ def _output_json(data: Any, stream=sys.stdout) -> None:
80
80
  @with_error_handling(
81
81
  fallback=None,
82
82
  suppress=(OSError, IOError, UnicodeEncodeError),
83
- context_provider=lambda: {"function": "pout"}
83
+ context_provider=lambda: {"function": "pout"},
84
84
  )
85
85
  def pout(message: Any, **kwargs: Any) -> None:
86
86
  """
@@ -142,7 +142,7 @@ def pout(message: Any, **kwargs: Any) -> None:
142
142
  @with_error_handling(
143
143
  fallback=None,
144
144
  suppress=(OSError, IOError, UnicodeEncodeError),
145
- context_provider=lambda: {"function": "perr"}
145
+ context_provider=lambda: {"function": "perr"},
146
146
  )
147
147
  def perr(message: Any, **kwargs: Any) -> None:
148
148
  """