Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Version 0.6.1 - 2026-02-23

- Add exponential backoff to calls toward the auth endpoint

## Version 0.6.0 - 2026-01-27

- Add update_validation method
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "cradl"
version = "0.6.0"
version = "0.6.1"
description = "Python SDK for Cradl"
authors = [{ name = "Cradl", email = "hello@cradl.ai" }]
readme = "README.md"
Expand Down
9 changes: 9 additions & 0 deletions src/cradl/backoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
import time
from typing import Optional, Union, Type, Callable

import requests # type: ignore
from requests.exceptions import RequestException # type: ignore


def fatal_code(e: RequestException):
if isinstance(e.response, requests.Response) and isinstance(e.response.status_code, int):
return 400 <= e.response.status_code < 500
raise e


def exponential_backoff(
exceptions: Union[tuple[Type[Exception]], Type[Exception]],
Expand Down
16 changes: 4 additions & 12 deletions src/cradl/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
from base64 import b64encode
from datetime import datetime
from pathlib import Path

from typing import Callable, Dict, List, Optional, Sequence, Union
from urllib.parse import urlparse, quote

import requests # type: ignore
from requests.exceptions import RequestException # type: ignore

from .credentials import Credentials, guess_credentials
from .backoff import exponential_backoff, fatal_code
from .content import parse_content
from .credentials import Credentials, guess_credentials
from .log import setup_logging
from .backoff import exponential_backoff
from .response import decode_response, TooManyRequestsException, EmptyRequestError


logger = setup_logging(__name__)
Content = Union[bytes, bytearray, str, Path, io.IOBase]
Queryparam = Union[str, List[str]]
Expand All @@ -35,12 +33,6 @@ def dictstrip(d):
return {k: v for k, v in d.items() if v is not None}


def _fatal_code(e: RequestException):
if isinstance(e.response, requests.Response) and isinstance(e.response.status_code, int):
return 400 <= e.response.status_code < 500
raise e


class Client:
"""A low level client to invoke api methods from Cradl."""
def __init__(self, credentials: Optional[Credentials] = None, profile=None):
Expand All @@ -49,7 +41,7 @@ def __init__(self, credentials: Optional[Credentials] = None, profile=None):
self.credentials = credentials or guess_credentials(profile)

@exponential_backoff(TooManyRequestsException, max_tries=4)
@exponential_backoff(RequestException, max_tries=3, giveup=_fatal_code)
@exponential_backoff(RequestException, max_tries=3, giveup=fatal_code)
def _make_request(
self,
requests_fn: Callable,
Expand Down Expand Up @@ -81,7 +73,7 @@ def _make_request(
return decode_response(response)

@exponential_backoff(TooManyRequestsException, max_tries=4)
@exponential_backoff(RequestException, max_tries=3, giveup=_fatal_code)
@exponential_backoff(RequestException, max_tries=3, giveup=fatal_code)
def _make_fileserver_request(
self,
requests_fn: Callable,
Expand Down
7 changes: 6 additions & 1 deletion src/cradl/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from typing import Optional, Tuple

import requests # type: ignore
from requests.exceptions import RequestException # type: ignore

from .backoff import exponential_backoff, fatal_code
from .log import setup_logging

from .response import TooManyRequestsException, BadRequest

logger = setup_logging(__name__)
NULL_TOKEN = '', 0
Expand Down Expand Up @@ -82,6 +84,9 @@ def access_token(self) -> str:

return access_token

# Backoff on BadRequest since Kinde seems to sometimes give bogus 400 responses
@exponential_backoff(exceptions=(TooManyRequestsException, BadRequest), max_tries=4) # type: ignore
@exponential_backoff(RequestException, max_tries=3, giveup=fatal_code)
def _get_client_credentials(self) -> Tuple[str, int]:
if any(endpoint in self.auth_endpoint for endpoint in ['auth.lucidtech.io', 'auth.cradl.ai', 'kinde.com']):
data = {
Expand Down