Skip to main content

tha-req-runner

CI codecov PyPI Python pre-commit wheel size

A Tabular Helper API library that wraps requests/httpx with thread-safe session reuse, automatic retries, and a normalized response dict. 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)

Alternatives

This library is intentionally limited in scope — it provides a thin, thread-safe wrapper around requests or httpx with automatic retries and a normalized response dict. If you need more control:

  • httpx — modern HTTP client with async support and HTTP/2 built in; use directly if you don't need the session wrapper or normalized response shape
  • requests — the underlying sync HTTP library; sufficient on its own for simple, single-threaded use
  • tenacity — standalone retry library that wraps any function; more configurable than urllib3's built-in retry for complex retry strategies

Choose this library when you need thread-safe sessions, automatic retries, and a normalized response dict that fits the tha-* error pattern — none of the alternatives give you all three out of the box.

Used by

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

License

MIT

Metadata

Release files for tha-req-runner 0.2.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tha-req-runner 0.2.8
File Size Uploaded
tha_req_runner-0.2.8.tar.gz 71.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tha-req-runner 0.2.8
File Interpreter ABI Platform
tha_req_runner-0.2.8-py3-none-any.whl Python 3 none any Details

Total release size: 78.2 kB

Release files / tha_req_runner-0.2.8.tar.gz

Download URL tha_req_runner-0.2.8.tar.gz
Size 71.0 kB
Tags Source
SHA-256 checksum
How to use checksums
213b0f241ed0856cb020469802cff78638ae9b32c140f6d79e5f55faa98a5e50
BLAKE2b-256 checksum
How to use checksums
5bc40f63374032b9e0d6cf46c091d7ae33f08f21f02cab7e206ca994872384f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

Release files / tha_req_runner-0.2.8-py3-none-any.whl

Download URL tha_req_runner-0.2.8-py3-none-any.whl
Size 7.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eb19c80626e8a56330e01163dca9649070f0ec85af7a056bb8de62b8065c0547
BLAKE2b-256 checksum
How to use checksums
3b8f634327677549c2dba9dd39576bd8f74c8fa7f650fd3d77cf258f41759cfd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page