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
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_forcelistandallowed_methodsare 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.localon the instance - Retry defaults:
total=3,backoff_factor=0.5(delays: 0.5s → 1s → 2s) - Retry statuses:
500,502,503,504by default (requests backend only) - POST not retried by default — pass
allowed_methodsexplicitly 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— usesThaReqas its HTTP transport layer
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tha_req_runner-0.2.3.tar.gz.
File metadata
- Download URL: tha_req_runner-0.2.3.tar.gz
- Upload date:
- Size: 44.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b75708cb14e7272eeba8e718c27e86f6b5aee179abb027509ba1939813bda448
|
|
| MD5 |
dc434f48d63600c84e05b70d600c6531
|
|
| BLAKE2b-256 |
18df357dce3fe400c9323c245dcdc68d9ecad03d2a1412ad9349f7d1aa3866b3
|
Provenance
The following attestation bundles were made for tha_req_runner-0.2.3.tar.gz:
Publisher:
publish.yml on tha-guy-nate/tha-req-runner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tha_req_runner-0.2.3.tar.gz -
Subject digest:
b75708cb14e7272eeba8e718c27e86f6b5aee179abb027509ba1939813bda448 - Sigstore transparency entry: 1842186405
- Sigstore integration time:
-
Permalink:
tha-guy-nate/tha-req-runner@b8d6b743ac4cc910566305c10d52aafadfdd6f5d -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/tha-guy-nate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b8d6b743ac4cc910566305c10d52aafadfdd6f5d -
Trigger Event:
push
-
Statement type:
File details
Details for the file tha_req_runner-0.2.3-py3-none-any.whl.
File metadata
- Download URL: tha_req_runner-0.2.3-py3-none-any.whl
- Upload date:
- Size: 5.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bb1ea21de534729f6caa531febd3ba0e8f914b48e9f57c3db5ff27cdf02a08c
|
|
| MD5 |
e11ad1587e86d91b4d798aa80bcaa5b0
|
|
| BLAKE2b-256 |
22c41b0ac2c156cd61d7b8ebd4a2285bae359f5c403c5f6cfad544ea0fdc4623
|
Provenance
The following attestation bundles were made for tha_req_runner-0.2.3-py3-none-any.whl:
Publisher:
publish.yml on tha-guy-nate/tha-req-runner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tha_req_runner-0.2.3-py3-none-any.whl -
Subject digest:
2bb1ea21de534729f6caa531febd3ba0e8f914b48e9f57c3db5ff27cdf02a08c - Sigstore transparency entry: 1842186713
- Sigstore integration time:
-
Permalink:
tha-guy-nate/tha-req-runner@b8d6b743ac4cc910566305c10d52aafadfdd6f5d -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/tha-guy-nate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b8d6b743ac4cc910566305c10d52aafadfdd6f5d -
Trigger Event:
push
-
Statement type: