Skip to content

yggdrasil.path._retry

_retry

Generic retry wrapper for remote-path SDK calls.

Only transient backend errors (InternalError, BadRequest, 500/503, socket timeouts) are retried — up to 4 times with a flat 1 s sleep between attempts.

Permission errors (PermissionDenied / 401 / 403, expired tokens, …) fail fast: they're deterministic from the caller's point of view — the principal genuinely lacks the grant, the token is dead, or the auth config is wrong. Sleeping and retrying just hides the real problem; the caller (or a higher-level recovery path like the credential-vending auto-grant in :mod:yggdrasil.databricks.aws) decides what to do.

Anything else (NotFound, AlreadyExists, FileNotFoundError, ValueError, …) is not retried either — those are deterministic.

This module deliberately avoids importing the boto / databricks SDK exception classes; it duck-types on the exception's class name and the optional response attribute that boto-style errors carry. That keeps the retry layer usable with mocks in tests.

is_transient

is_transient(exc: BaseException) -> bool

True when exc should be retried as a transient backend error.

is_permission

is_permission(exc: BaseException) -> bool

True when exc looks like an auth / permission failure.

retry_sdk_call

retry_sdk_call(
    func: Callable[..., _T],
    *args: Any,
    max_transient_retries: int = 4,
    base_sleep: float = 1.0,
    sleep: Callable[[float], None] = time.sleep,
    **kwargs: Any
) -> _T

Call func(args, *kwargs) with the Databricks/AWS retry policy.

Sleeps a flat base_sleep seconds between transient retries (no exponential backoff — transient SDK errors recover fast on a healthy backend, and a long ceiling just stalls the caller). Permission errors fail fast — they're deterministic from the SDK's perspective and any recovery (self-grant, owner takeover, …) belongs in a higher-level handler.

The sleep callable is injected so tests can pass a no-op or a spy. The default is :func:time.sleep.

Non-transient errors propagate immediately — that includes :class:PermissionDenied, :class:FileNotFoundError, :class:ValueError, :class:KeyError, and any custom error type the caller wants to surface deterministically.