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.
- provide/foundation/__init__.py +29 -3
- provide/foundation/archive/operations.py +4 -6
- provide/foundation/cli/__init__.py +2 -2
- provide/foundation/cli/commands/deps.py +13 -7
- provide/foundation/cli/commands/logs/__init__.py +1 -1
- provide/foundation/cli/commands/logs/query.py +1 -1
- provide/foundation/cli/commands/logs/send.py +1 -1
- provide/foundation/cli/commands/logs/tail.py +1 -1
- provide/foundation/cli/decorators.py +11 -10
- provide/foundation/cli/main.py +1 -1
- provide/foundation/cli/testing.py +2 -35
- provide/foundation/cli/utils.py +21 -17
- provide/foundation/config/__init__.py +35 -2
- provide/foundation/config/converters.py +479 -0
- provide/foundation/config/defaults.py +67 -0
- provide/foundation/config/env.py +4 -19
- provide/foundation/config/loader.py +9 -3
- provide/foundation/console/input.py +5 -5
- provide/foundation/console/output.py +35 -13
- provide/foundation/context/__init__.py +8 -4
- provide/foundation/context/core.py +85 -109
- provide/foundation/crypto/certificates/operations.py +1 -1
- provide/foundation/errors/__init__.py +2 -3
- provide/foundation/errors/decorators.py +0 -231
- provide/foundation/errors/types.py +0 -97
- provide/foundation/file/directory.py +13 -22
- provide/foundation/file/lock.py +3 -1
- provide/foundation/hub/components.py +72 -384
- provide/foundation/hub/config.py +151 -0
- provide/foundation/hub/discovery.py +62 -0
- provide/foundation/hub/handlers.py +81 -0
- provide/foundation/hub/lifecycle.py +194 -0
- provide/foundation/hub/manager.py +4 -4
- provide/foundation/hub/processors.py +44 -0
- provide/foundation/integrations/__init__.py +11 -0
- provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
- provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/client.py +12 -12
- provide/foundation/{observability → integrations}/openobserve/commands.py +3 -3
- provide/foundation/integrations/openobserve/config.py +37 -0
- provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/otlp.py +1 -1
- provide/foundation/{observability → integrations}/openobserve/search.py +2 -2
- provide/foundation/{observability → integrations}/openobserve/streaming.py +4 -4
- provide/foundation/logger/config/logging.py +68 -298
- provide/foundation/logger/config/telemetry.py +41 -121
- provide/foundation/logger/setup/coordinator.py +1 -1
- provide/foundation/observability/__init__.py +2 -2
- provide/foundation/process/__init__.py +9 -0
- provide/foundation/process/exit.py +47 -0
- provide/foundation/process/lifecycle.py +33 -33
- provide/foundation/resilience/__init__.py +35 -0
- provide/foundation/resilience/circuit.py +164 -0
- provide/foundation/resilience/decorators.py +220 -0
- provide/foundation/resilience/fallback.py +193 -0
- provide/foundation/resilience/retry.py +325 -0
- provide/foundation/streams/config.py +79 -0
- provide/foundation/streams/console.py +7 -8
- provide/foundation/streams/core.py +6 -3
- provide/foundation/streams/file.py +12 -2
- provide/foundation/testing/__init__.py +7 -2
- provide/foundation/testing/cli.py +30 -17
- provide/foundation/testing/common/__init__.py +0 -2
- provide/foundation/testing/common/fixtures.py +0 -27
- provide/foundation/testing/file/content_fixtures.py +316 -0
- provide/foundation/testing/file/directory_fixtures.py +107 -0
- provide/foundation/testing/file/fixtures.py +45 -516
- provide/foundation/testing/file/special_fixtures.py +153 -0
- provide/foundation/testing/logger.py +76 -0
- provide/foundation/testing/process/async_fixtures.py +405 -0
- provide/foundation/testing/process/fixtures.py +50 -571
- provide/foundation/testing/process/subprocess_fixtures.py +209 -0
- provide/foundation/testing/threading/basic_fixtures.py +101 -0
- provide/foundation/testing/threading/data_fixtures.py +99 -0
- provide/foundation/testing/threading/execution_fixtures.py +263 -0
- provide/foundation/testing/threading/fixtures.py +34 -500
- provide/foundation/testing/threading/sync_fixtures.py +97 -0
- provide/foundation/testing/time/fixtures.py +4 -4
- provide/foundation/tools/cache.py +8 -6
- provide/foundation/tools/downloader.py +23 -12
- provide/foundation/tracer/spans.py +2 -2
- provide/foundation/transport/config.py +26 -95
- provide/foundation/transport/middleware.py +30 -36
- provide/foundation/utils/deps.py +14 -12
- provide/foundation/utils/parsing.py +49 -4
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/METADATA +1 -1
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/RECORD +93 -68
- /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.dev2.dist-info}/WHEEL +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/entry_points.txt +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
- {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev2.dist-info}/top_level.txt +0 -0
@@ -7,13 +7,9 @@ fallback, and error suppression.
|
|
7
7
|
from collections.abc import Callable
|
8
8
|
import functools
|
9
9
|
import inspect
|
10
|
-
import time
|
11
10
|
from typing import Any, TypeVar
|
12
11
|
|
13
|
-
from attrs import define, field
|
14
|
-
|
15
12
|
from provide.foundation.errors.base import FoundationError
|
16
|
-
from provide.foundation.errors.types import RetryPolicy
|
17
13
|
|
18
14
|
F = TypeVar("F", bound=Callable[..., Any])
|
19
15
|
|
@@ -148,125 +144,6 @@ def with_error_handling(
|
|
148
144
|
return decorator(func)
|
149
145
|
|
150
146
|
|
151
|
-
def retry_on_error(
|
152
|
-
*exceptions: type[Exception],
|
153
|
-
policy: RetryPolicy | None = None,
|
154
|
-
max_attempts: int | None = None,
|
155
|
-
delay: float | None = None,
|
156
|
-
backoff: float | None = None,
|
157
|
-
on_retry: Callable[[int, Exception], None] | None = None,
|
158
|
-
) -> Callable[[F], F]:
|
159
|
-
"""Decorator for retrying operations on specific errors.
|
160
|
-
|
161
|
-
Args:
|
162
|
-
*exceptions: Exception types to retry on (all if empty).
|
163
|
-
policy: Complete retry policy (overrides other retry params).
|
164
|
-
max_attempts: Maximum retry attempts (ignored if policy provided).
|
165
|
-
delay: Base delay between retries in seconds.
|
166
|
-
backoff: Backoff multiplier for delays.
|
167
|
-
on_retry: Callback function called before each retry.
|
168
|
-
|
169
|
-
Returns:
|
170
|
-
Decorated function.
|
171
|
-
|
172
|
-
Examples:
|
173
|
-
>>> @retry_on_error(ConnectionError, TimeoutError, max_attempts=3)
|
174
|
-
... def fetch_data():
|
175
|
-
... return api_call()
|
176
|
-
|
177
|
-
>>> @retry_on_error(
|
178
|
-
... policy=RetryPolicy(max_attempts=5, backoff="exponential")
|
179
|
-
... )
|
180
|
-
... def unreliable_operation():
|
181
|
-
... pass
|
182
|
-
"""
|
183
|
-
# Use provided policy or create one from parameters
|
184
|
-
if policy is None:
|
185
|
-
from provide.foundation.errors.types import BackoffStrategy
|
186
|
-
|
187
|
-
# Determine backoff strategy
|
188
|
-
if backoff is not None and backoff > 1:
|
189
|
-
backoff_strategy = BackoffStrategy.EXPONENTIAL
|
190
|
-
elif backoff == 1:
|
191
|
-
backoff_strategy = BackoffStrategy.FIXED
|
192
|
-
else:
|
193
|
-
backoff_strategy = BackoffStrategy.EXPONENTIAL
|
194
|
-
|
195
|
-
policy = RetryPolicy(
|
196
|
-
max_attempts=max_attempts or 3,
|
197
|
-
base_delay=delay or 1.0,
|
198
|
-
backoff=backoff_strategy,
|
199
|
-
retryable_errors=exceptions if exceptions else None,
|
200
|
-
)
|
201
|
-
|
202
|
-
def decorator(func: F) -> F:
|
203
|
-
@functools.wraps(func)
|
204
|
-
def wrapper(*args, **kwargs):
|
205
|
-
last_exception = None
|
206
|
-
|
207
|
-
for attempt in range(1, policy.max_attempts + 1):
|
208
|
-
try:
|
209
|
-
return func(*args, **kwargs)
|
210
|
-
except Exception as e:
|
211
|
-
last_exception = e
|
212
|
-
|
213
|
-
# Check if we should retry this error
|
214
|
-
if not policy.should_retry(e, attempt):
|
215
|
-
if attempt > 1: # Only log if we've actually retried
|
216
|
-
_get_logger().error(
|
217
|
-
f"All {attempt} retry attempts failed for {func.__name__}",
|
218
|
-
attempts=attempt,
|
219
|
-
error=str(e),
|
220
|
-
error_type=type(e).__name__,
|
221
|
-
)
|
222
|
-
raise
|
223
|
-
|
224
|
-
# Don't retry on last attempt
|
225
|
-
if attempt >= policy.max_attempts:
|
226
|
-
_get_logger().error(
|
227
|
-
f"All {policy.max_attempts} retry attempts failed for {func.__name__}",
|
228
|
-
attempts=policy.max_attempts,
|
229
|
-
error=str(e),
|
230
|
-
error_type=type(e).__name__,
|
231
|
-
)
|
232
|
-
raise
|
233
|
-
|
234
|
-
# Calculate delay
|
235
|
-
retry_delay = policy.calculate_delay(attempt)
|
236
|
-
|
237
|
-
# Log retry attempt
|
238
|
-
_get_logger().warning(
|
239
|
-
f"Retry {attempt}/{policy.max_attempts} for {func.__name__} after {retry_delay:.2f}s",
|
240
|
-
function=func.__name__,
|
241
|
-
attempt=attempt,
|
242
|
-
max_attempts=policy.max_attempts,
|
243
|
-
delay=retry_delay,
|
244
|
-
error=str(e),
|
245
|
-
error_type=type(e).__name__,
|
246
|
-
)
|
247
|
-
|
248
|
-
# Call retry callback if provided
|
249
|
-
if on_retry:
|
250
|
-
try:
|
251
|
-
on_retry(attempt, e)
|
252
|
-
except Exception as callback_error:
|
253
|
-
_get_logger().warning(
|
254
|
-
f"Retry callback failed: {callback_error}",
|
255
|
-
function=func.__name__,
|
256
|
-
attempt=attempt,
|
257
|
-
)
|
258
|
-
|
259
|
-
# Wait before retry
|
260
|
-
time.sleep(retry_delay)
|
261
|
-
|
262
|
-
# Should never reach here, but just in case
|
263
|
-
if last_exception:
|
264
|
-
raise last_exception
|
265
|
-
|
266
|
-
return wrapper # type: ignore
|
267
|
-
|
268
|
-
return decorator
|
269
|
-
|
270
147
|
|
271
148
|
def suppress_and_log(
|
272
149
|
*exceptions: type[Exception],
|
@@ -374,111 +251,3 @@ def fallback_on_error(
|
|
374
251
|
return decorator
|
375
252
|
|
376
253
|
|
377
|
-
@define(kw_only=True, slots=True)
|
378
|
-
class CircuitBreaker:
|
379
|
-
"""Circuit breaker pattern for preventing cascading failures.
|
380
|
-
|
381
|
-
Attributes:
|
382
|
-
failure_threshold: Number of failures before opening circuit.
|
383
|
-
recovery_timeout: Seconds to wait before attempting recovery.
|
384
|
-
expected_exception: Exception types that trigger the breaker.
|
385
|
-
"""
|
386
|
-
|
387
|
-
failure_threshold: int = 5
|
388
|
-
recovery_timeout: float = 60.0
|
389
|
-
expected_exception: tuple[type[Exception], ...] = field(default=(Exception,))
|
390
|
-
|
391
|
-
# Internal state
|
392
|
-
_failure_count: int = field(init=False, default=0)
|
393
|
-
_last_failure_time: float | None = field(init=False, default=None)
|
394
|
-
_state: str = field(init=False, default="closed") # closed, open, half_open
|
395
|
-
|
396
|
-
def __call__(self, func: F) -> F:
|
397
|
-
"""Decorator to apply circuit breaker to a function."""
|
398
|
-
|
399
|
-
@functools.wraps(func)
|
400
|
-
def wrapper(*args, **kwargs):
|
401
|
-
# Check circuit state
|
402
|
-
if self._state == "open":
|
403
|
-
# Check if we should try half-open
|
404
|
-
if (
|
405
|
-
self._last_failure_time
|
406
|
-
and (time.time() - self._last_failure_time) > self.recovery_timeout
|
407
|
-
):
|
408
|
-
self._state = "half_open"
|
409
|
-
_get_logger().info(
|
410
|
-
f"Circuit breaker for {func.__name__} entering half-open state",
|
411
|
-
function=func.__name__,
|
412
|
-
)
|
413
|
-
else:
|
414
|
-
raise RuntimeError(f"Circuit breaker is open for {func.__name__}")
|
415
|
-
|
416
|
-
try:
|
417
|
-
result = func(*args, **kwargs)
|
418
|
-
|
419
|
-
# Success - reset on half-open or reduce failure count
|
420
|
-
if self._state == "half_open":
|
421
|
-
self._state = "closed"
|
422
|
-
self._failure_count = 0
|
423
|
-
_get_logger().info(
|
424
|
-
f"Circuit breaker for {func.__name__} closed after successful recovery",
|
425
|
-
function=func.__name__,
|
426
|
-
)
|
427
|
-
elif self._failure_count > 0:
|
428
|
-
self._failure_count = max(0, self._failure_count - 1)
|
429
|
-
|
430
|
-
return result
|
431
|
-
|
432
|
-
except self.expected_exception as e:
|
433
|
-
self._failure_count += 1
|
434
|
-
self._last_failure_time = time.time()
|
435
|
-
|
436
|
-
# Check if we should open the circuit
|
437
|
-
if self._failure_count >= self.failure_threshold:
|
438
|
-
self._state = "open"
|
439
|
-
_get_logger().error(
|
440
|
-
f"Circuit breaker for {func.__name__} opened after {self._failure_count} failures",
|
441
|
-
function=func.__name__,
|
442
|
-
failures=self._failure_count,
|
443
|
-
error=str(e),
|
444
|
-
)
|
445
|
-
else:
|
446
|
-
_get_logger().warning(
|
447
|
-
f"Circuit breaker for {func.__name__} failure {self._failure_count}/{self.failure_threshold}",
|
448
|
-
function=func.__name__,
|
449
|
-
failures=self._failure_count,
|
450
|
-
threshold=self.failure_threshold,
|
451
|
-
error=str(e),
|
452
|
-
)
|
453
|
-
|
454
|
-
raise
|
455
|
-
|
456
|
-
return wrapper # type: ignore
|
457
|
-
|
458
|
-
|
459
|
-
def circuit_breaker(
|
460
|
-
failure_threshold: int = 5,
|
461
|
-
recovery_timeout: float = 60.0,
|
462
|
-
expected_exception: tuple[type[Exception], ...] = (Exception,),
|
463
|
-
) -> Callable[[F], F]:
|
464
|
-
"""Create a circuit breaker decorator.
|
465
|
-
|
466
|
-
Args:
|
467
|
-
failure_threshold: Number of failures before opening circuit.
|
468
|
-
recovery_timeout: Seconds to wait before attempting recovery.
|
469
|
-
expected_exception: Exception types that trigger the breaker.
|
470
|
-
|
471
|
-
Returns:
|
472
|
-
Circuit breaker decorator.
|
473
|
-
|
474
|
-
Examples:
|
475
|
-
>>> @circuit_breaker(failure_threshold=3, recovery_timeout=30)
|
476
|
-
... def unreliable_service():
|
477
|
-
... return external_api_call()
|
478
|
-
"""
|
479
|
-
breaker = CircuitBreaker(
|
480
|
-
failure_threshold=failure_threshold,
|
481
|
-
recovery_timeout=recovery_timeout,
|
482
|
-
expected_exception=expected_exception,
|
483
|
-
)
|
484
|
-
return breaker
|
@@ -118,103 +118,6 @@ class ErrorMetadata:
|
|
118
118
|
return result
|
119
119
|
|
120
120
|
|
121
|
-
class BackoffStrategy(str, Enum):
|
122
|
-
"""Backoff strategies for retry policies."""
|
123
|
-
|
124
|
-
FIXED = "fixed" # Fixed delay between retries
|
125
|
-
LINEAR = "linear" # Linear increase (delay * attempt)
|
126
|
-
EXPONENTIAL = "exponential" # Exponential increase (delay * 2^attempt)
|
127
|
-
FIBONACCI = "fibonacci" # Fibonacci sequence delays
|
128
|
-
|
129
|
-
|
130
|
-
@define(kw_only=True, slots=True)
|
131
|
-
class RetryPolicy:
|
132
|
-
"""Configuration for retry behavior on errors.
|
133
|
-
|
134
|
-
Attributes:
|
135
|
-
max_attempts: Maximum number of retry attempts.
|
136
|
-
backoff: Backoff strategy to use.
|
137
|
-
base_delay: Base delay in seconds between retries.
|
138
|
-
max_delay: Maximum delay in seconds (caps exponential growth).
|
139
|
-
jitter: Whether to add random jitter to delays.
|
140
|
-
retryable_errors: Optional tuple of exception types to retry on.
|
141
|
-
|
142
|
-
Examples:
|
143
|
-
>>> policy = RetryPolicy(
|
144
|
-
... max_attempts=5,
|
145
|
-
... backoff=BackoffStrategy.EXPONENTIAL,
|
146
|
-
... base_delay=1.0,
|
147
|
-
... max_delay=30.0
|
148
|
-
... )
|
149
|
-
"""
|
150
|
-
|
151
|
-
max_attempts: int = 3
|
152
|
-
backoff: BackoffStrategy = BackoffStrategy.EXPONENTIAL
|
153
|
-
base_delay: float = 1.0
|
154
|
-
max_delay: float = 60.0
|
155
|
-
jitter: bool = True
|
156
|
-
retryable_errors: tuple[type[Exception], ...] | None = None
|
157
|
-
|
158
|
-
def calculate_delay(self, attempt: int) -> float:
|
159
|
-
"""Calculate delay for a given attempt number.
|
160
|
-
|
161
|
-
Args:
|
162
|
-
attempt: Attempt number (1-based).
|
163
|
-
|
164
|
-
Returns:
|
165
|
-
Delay in seconds.
|
166
|
-
"""
|
167
|
-
if attempt <= 0:
|
168
|
-
return 0
|
169
|
-
|
170
|
-
if self.backoff == BackoffStrategy.FIXED:
|
171
|
-
delay = self.base_delay
|
172
|
-
elif self.backoff == BackoffStrategy.LINEAR:
|
173
|
-
delay = self.base_delay * attempt
|
174
|
-
elif self.backoff == BackoffStrategy.EXPONENTIAL:
|
175
|
-
delay = self.base_delay * (2 ** (attempt - 1))
|
176
|
-
elif self.backoff == BackoffStrategy.FIBONACCI:
|
177
|
-
# Calculate fibonacci number for attempt
|
178
|
-
a, b = 0, 1
|
179
|
-
for _ in range(attempt):
|
180
|
-
a, b = b, a + b
|
181
|
-
delay = self.base_delay * a
|
182
|
-
else:
|
183
|
-
delay = self.base_delay
|
184
|
-
|
185
|
-
# Cap at max delay
|
186
|
-
delay = min(delay, self.max_delay)
|
187
|
-
|
188
|
-
# Add jitter if configured (±25% random variation)
|
189
|
-
if self.jitter:
|
190
|
-
import random
|
191
|
-
|
192
|
-
jitter_factor = 0.75 + (random.random() * 0.5)
|
193
|
-
delay *= jitter_factor
|
194
|
-
|
195
|
-
return delay
|
196
|
-
|
197
|
-
def should_retry(self, error: Exception, attempt: int) -> bool:
|
198
|
-
"""Determine if an error should be retried.
|
199
|
-
|
200
|
-
Args:
|
201
|
-
error: The exception that occurred.
|
202
|
-
attempt: Current attempt number (1-based).
|
203
|
-
|
204
|
-
Returns:
|
205
|
-
True if should retry, False otherwise.
|
206
|
-
"""
|
207
|
-
# Check attempt limit
|
208
|
-
if attempt >= self.max_attempts:
|
209
|
-
return False
|
210
|
-
|
211
|
-
# Check error type if filter is configured
|
212
|
-
if self.retryable_errors is not None:
|
213
|
-
return isinstance(error, self.retryable_errors)
|
214
|
-
|
215
|
-
# Default to retry for any error
|
216
|
-
return True
|
217
|
-
|
218
121
|
|
219
122
|
@define(kw_only=True, slots=True)
|
220
123
|
class ErrorResponse:
|
@@ -6,6 +6,8 @@ from pathlib import Path
|
|
6
6
|
import shutil
|
7
7
|
import tempfile
|
8
8
|
|
9
|
+
from provide.foundation.errors.decorators import with_error_handling
|
10
|
+
from provide.foundation.errors.handlers import error_boundary
|
9
11
|
from provide.foundation.logger import get_logger
|
10
12
|
|
11
13
|
log = get_logger(__name__)
|
@@ -80,17 +82,12 @@ def temp_dir(
|
|
80
82
|
yield temp_path
|
81
83
|
finally:
|
82
84
|
if cleanup and temp_path and temp_path.exists():
|
83
|
-
|
85
|
+
with error_boundary(Exception, reraise=False):
|
84
86
|
shutil.rmtree(temp_path)
|
85
87
|
log.debug("Cleaned up temp directory", path=str(temp_path))
|
86
|
-
except Exception as e:
|
87
|
-
log.warning(
|
88
|
-
"Failed to cleanup temp directory",
|
89
|
-
path=str(temp_path),
|
90
|
-
error=str(e),
|
91
|
-
)
|
92
88
|
|
93
89
|
|
90
|
+
@with_error_handling(fallback=False, suppress=(FileNotFoundError,) if False else ())
|
94
91
|
def safe_rmtree(
|
95
92
|
path: Path | str,
|
96
93
|
missing_ok: bool = True,
|
@@ -109,21 +106,15 @@ def safe_rmtree(
|
|
109
106
|
"""
|
110
107
|
path = Path(path)
|
111
108
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
raise FileNotFoundError(f"Directory does not exist: {path}")
|
122
|
-
except Exception as e:
|
123
|
-
if not path.exists() and missing_ok:
|
124
|
-
return False
|
125
|
-
log.error("Failed to remove directory tree", path=str(path), error=str(e))
|
126
|
-
raise
|
109
|
+
if path.exists():
|
110
|
+
shutil.rmtree(path)
|
111
|
+
log.debug("Removed directory tree", path=str(path))
|
112
|
+
return True
|
113
|
+
elif missing_ok:
|
114
|
+
log.debug("Directory already absent", path=str(path))
|
115
|
+
return False
|
116
|
+
else:
|
117
|
+
raise FileNotFoundError(f"Directory does not exist: {path}")
|
127
118
|
|
128
119
|
|
129
120
|
__all__ = [
|
provide/foundation/file/lock.py
CHANGED
@@ -4,6 +4,8 @@ import os
|
|
4
4
|
from pathlib import Path
|
5
5
|
import time
|
6
6
|
|
7
|
+
from provide.foundation.config.defaults import DEFAULT_FILE_LOCK_TIMEOUT
|
8
|
+
from provide.foundation.errors.decorators import with_error_handling
|
7
9
|
from provide.foundation.errors.resources import LockError
|
8
10
|
from provide.foundation.logger import get_logger
|
9
11
|
|
@@ -25,7 +27,7 @@ class FileLock:
|
|
25
27
|
def __init__(
|
26
28
|
self,
|
27
29
|
path: Path | str,
|
28
|
-
timeout: float =
|
30
|
+
timeout: float = DEFAULT_FILE_LOCK_TIMEOUT,
|
29
31
|
check_interval: float = 0.1,
|
30
32
|
) -> None:
|
31
33
|
"""Initialize file lock.
|