Skip to content
Open
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
13 changes: 5 additions & 8 deletions py/src/braintrust/api/_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
BraintrustTransportError,
BraintrustTransportRetryExhaustedError,
)
from .policies import RetryMode, RetryPolicy
from .policies import RetryMode, RetryPolicy, is_retryable_request_exception


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -120,6 +120,9 @@ def make_long_lived(self) -> None:
)
self._reset()

def close(self) -> None:
self.session.close()

@staticmethod
def sanitize_token(token: str) -> str:
return token.rstrip("\n")
Expand Down Expand Up @@ -272,7 +275,7 @@ def request(
**kwargs,
)
except requests.exceptions.RequestException as exc:
if not _is_retryable_request_exception(exc):
if not is_retryable_request_exception(exc):
error = BraintrustTransportError(method=method, url=url, attempts=attempt, retryable=False)
raise error from exc
if attempt >= max_attempts:
Expand Down Expand Up @@ -396,12 +399,6 @@ def _request_body_is_replayable(data: Any, files: Any) -> bool:
return files is None and (data is None or isinstance(data, (bytes, str)))


def _is_retryable_request_exception(exc: requests.exceptions.RequestException) -> bool:
return isinstance(exc, (requests.exceptions.ConnectionError, requests.exceptions.Timeout)) and not isinstance(
exc, requests.exceptions.SSLError
)


def _parse_retry_after(value: str | None, wall_time: float) -> float | None:
if value is None:
return None
Expand Down
9 changes: 9 additions & 0 deletions py/src/braintrust/api/policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import enum
from dataclasses import dataclass

import requests


DEFAULT_RETRYABLE_STATUSES = frozenset({408, 429, 500, 502, 503, 504})
DEFAULT_MAX_ATTEMPTS = 4
Expand All @@ -11,6 +13,13 @@
DEFAULT_MAX_BACKOFF = 10.0


def is_retryable_request_exception(exc: requests.exceptions.RequestException) -> bool:
"""Return whether a requests transport failure is safe to retry."""
return isinstance(exc, (requests.exceptions.ConnectionError, requests.exceptions.Timeout)) and not isinstance(
exc, requests.exceptions.SSLError
)


class RetryMode(enum.Enum):
"""The replay safety classification for an API operation."""

Expand Down
Loading