Skip to content

Observability & Custom Retry Gating

TusClient exposes three lightweight hooks for tracing requests and shaping retry behaviour. They cost nothing when unset.

before_request and after_response

def before(method, url, headers):
    print(f"-> {method} {url}")


def after(method, url, status):
    print(f"<- {method} {url} {status}")


client = TusClient(
    "http://localhost:8080/files",
    before_request=before,
    after_response=after,
)

before_request fires before every HTTP request the client issues: the creation POST (plain, partial and final), every HEAD (offset checks, get_upload_info, get_metadata), every PATCH, OPTIONS (get_server_info) and DELETE. after_response fires after each response with its status code. For uploader-routed requests (the offset HEAD and every PATCH) it fires only on success, in both the sync and the async client; for client-level calls (create, final, info, metadata, server info, delete) the sync client also reaches it only for non-error responses, since urlopen raises on 4xx/5xx, while the async client reports every status. They run on the calling thread, synchronously — keep them cheap (logging, metrics, span creation) and avoid blocking I/O.

The hooks are forwarded to any Uploader you obtain from TusClient.create_uploader(), so chunk PATCHes are observed too.

on_should_retry

def should_retry(err: Exception, attempt: int) -> bool:
    if isinstance(err, PermissionError):
        return False  # don't retry auth failures
    return attempt < 5  # cap above the configured max_retries


client = TusClient(
    "http://localhost:8080/files",
    max_retries=10,
    on_should_retry=should_retry,
)

on_should_retry is consulted before every retry attempt during a chunk upload. Returning False aborts retry immediately and surfaces a TusUploadFailed. Returning True falls through to the standard exponential-backoff schedule.

The hook receives the original exception and the 1-indexed attempt number, so it can apply domain rules ("don't retry quota errors", "give the network exactly one extra try") without touching the library's retry math.

Examples

  • examples/client/hooks.py — full hook set wired into a TusClient.