Skip to main content

A Tabular Helper API library that wraps requests with thread-safe session reuse, automatic retries, and a normalized response dict.

Project description

tha-req-runner

CI

A small Python library that provides a thread-safe HTTP session with automatic retries and a normalized response parser. Supports both requests (default) and httpx backends. Intended as the HTTP transport layer for other tha-* runners.

Install

pip install tha-req-runner           # requests backend (default)
pip install tha-req-runner[httpx]    # adds httpx backend support

Quick start

from tha_req_runner import ThaReq

# requests backend (default)
req = ThaReq()
session = req.get_session()

# httpx backend
req = ThaReq(backend="httpx")
client = req.get_session()

# safe_call wraps the try/except for you — same API regardless of backend
result = req.safe_call(session.get, "https://api.example.com/students", params={"limit": 100})
# {"status": None, "code": 200, "data": [...], "message": None, "raw_response": <Response>}

# network errors return the same shape — no try/except needed
result = req.safe_call(session.get, "https://unreachable.example.com")
# {"status": "error", "code": None, "data": None, "message": "Connection refused", "raw_response": None}

Response dict

Every call returns the same shape whether it succeeded or raised:

Key Type Description
status "error" | None "error" on any failure (HTTP error or network error). None on success
code int | None HTTP status code, or None on network error
data object Parsed JSON body. Populated on success and on HTTP errors if the API returned a JSON error body
message str | None HTTP error or exception message. None on success
raw_response Response | None The raw response object (requests.Response or httpx.Response)

safe_call automatically calls raise_for_status(), so 4xx/5xx responses are treated as errors:

# 200 → success path
{"status": None, "code": 200, "data": {"id": 1}, "message": None, "raw_response": <Response>}

# 422 with JSON error body → error path, data preserved
{"status": "error", "code": 422, "data": {"detail": "field required"}, "message": "422 Unprocessable Entity", "raw_response": <Response>}

# network error → no code or data
{"status": "error", "code": None, "data": None, "message": "Connection refused", "raw_response": None}

API

ThaReq

ThaReq(*, backend: Literal["requests", "httpx"] = "requests")

backend="httpx" requires pip install tha-req-runner[httpx].

req.get_session()

req.get_session(
    *,
    status_forcelist: tuple[int, ...] = (500, 502, 503, 504),  # requests only
    allowed_methods: Collection[str] | None = None,             # requests only
    headers: dict[str, str] | None = None,
    timeout: float = 30,
) -> requests.Session | httpx.Client

Returns a session configured with automatic retries. Config is applied only on the first call per thread — subsequent calls on the same thread return the cached session regardless of args. Two ThaReq instances never share a session.

allowed_methods=None uses urllib3's safe-method default, which excludes POST. To retry POST (e.g. token endpoints):

session = req.get_session(
    status_forcelist=(429, 500, 502, 503, 504),
    allowed_methods=frozenset(["GET", "POST"]),
)

httpx note: status_forcelist and allowed_methods are ignored for the httpx backend. httpx retry is connection-level only (no status-based retry).

req.reset_session() / req.close_session()

Closes and discards the current thread's session. The next get_session() call creates a fresh one. Useful when auth tokens rotate or a session enters a bad state. Both methods are equivalent.

ThaReq.parse_response()

ThaReq.parse_response(result) -> dict[str, Any]

Normalizes a response object or a caught exception into a consistent dict. Works with both requests.Response and httpx.Response. Also callable as an instance method.

req.safe_call()

req.safe_call(fn, *args, **kwargs) -> dict[str, Any]

Calls fn(*args, **kwargs), calls raise_for_status() on the response, catches any exception, and returns a normalized response dict. Automatically injects the session timeout unless the caller provides one. JSON error bodies from 4xx/5xx responses are preserved in data.

result = req.safe_call(session.get, url, params={"limit": 100})
result = req.safe_call(session.post, token_url, data={"grant_type": "client_credentials"})
result = req.safe_call(session.get, url, timeout=5)  # override per-call

Session and retries

  • Thread-safe: each thread gets its own session via threading.local on the instance
  • Retry defaults: total=3, backoff_factor=0.5 (delays: 0.5s → 1s → 2s)
  • Retry statuses: 500, 502, 503, 504 by default (requests backend only)
  • POST not retried by default — pass allowed_methods explicitly to enable it (requests backend only)
  • Default timeout: 30s, injected automatically by safe_call
  • Sessions are reused across calls on the same thread

Backend comparison

Feature requests (default) httpx
Status-based retry Yes (status_forcelist) No
Allowed methods config Yes No
Default timeout Yes Yes
Default headers Yes Yes
Thread-safe sessions Yes Yes
HTTP/2 No Yes
Async support No Yes (use httpx.AsyncClient directly)

Used by

  • tha-edfi-runner — uses ThaReq as its HTTP transport layer

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tha_req_runner-0.2.2.tar.gz (6.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tha_req_runner-0.2.2-py3-none-any.whl (5.4 kB view details)

Uploaded Python 3

File details

Details for the file tha_req_runner-0.2.2.tar.gz.

File metadata

  • Download URL: tha_req_runner-0.2.2.tar.gz
  • Upload date:
  • Size: 6.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tha_req_runner-0.2.2.tar.gz
Algorithm Hash digest
SHA256 03a4c1d1a4c7ff3f18881045d6a92043ced5f91d125734cd4839d1b7e5186b3a
MD5 25c973191ac55ab55d149d0ab2d56251
BLAKE2b-256 e3e550ea4fa14f7edb0d163826af60f1029e7ab9f805df566b140b6120f54f68

See more details on using hashes here.

Provenance

The following attestation bundles were made for tha_req_runner-0.2.2.tar.gz:

Publisher: publish.yml on tha-guy-nate/tha-req-runner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tha_req_runner-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: tha_req_runner-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 5.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tha_req_runner-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c480117d53ddfce754a3add012e3b3fe6e8e8af2f43fbd3fdf310564bd8af996
MD5 fdde78f9d1c9f1ab08473e3261caab62
BLAKE2b-256 4efe54c23110459c95874e662a0065a872a11917467fdbabdd4abae256161859

See more details on using hashes here.

Provenance

The following attestation bundles were made for tha_req_runner-0.2.2-py3-none-any.whl:

Publisher: publish.yml on tha-guy-nate/tha-req-runner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page