Client & Response¶
AlmaAPIClient¶
The main HTTP client for the Alma REST API. Construct it for a given environment, then hand it to a domain class.
AlmaAPIClient
¶
AlmaAPIClient(
environment: str = "SANDBOX",
*,
api_key: Optional[str] = None,
max_retries: int = DEFAULT_RETRY_TOTAL,
backoff_factor: float = DEFAULT_RETRY_BACKOFF_FACTOR,
retry: Optional[Retry] = None,
timeout: Optional[float] = None,
region: str = DEFAULT_REGION,
host: Optional[str] = None
)
General abstract gateway to the Alma API.
This class provides: - Environment management (SANDBOX/PRODUCTION) - Core HTTP methods (GET, POST, PUT, DELETE) - Authentication handling - Connection testing - Foundation for pluggable domain-specific classes
This class is designed to be inherited or composed by specific domain classes (BiblioGraphicRecords, Users, Admin, etc.)
Initialize the API client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
environment
|
str
|
'SANDBOX' or 'PRODUCTION'. |
'SANDBOX'
|
api_key
|
Optional[str]
|
Explicit API key. When given it is used verbatim and
takes precedence over the environment variable. When
|
None
|
max_retries
|
int
|
Total number of retries the mounted HTTPAdapter
should attempt on retryable responses (429 and 5xx).
Must be an |
DEFAULT_RETRY_TOTAL
|
backoff_factor
|
float
|
Exponential backoff multiplier passed to
|
DEFAULT_RETRY_BACKOFF_FACTOR
|
retry
|
Optional[Retry]
|
Optional fully-built |
None
|
timeout
|
Optional[float]
|
Default per-request timeout in seconds. |
None
|
region
|
str
|
Alma hosting region key. Must be one of the keys in
|
DEFAULT_REGION
|
host
|
Optional[str]
|
Override the resolved base URL with an arbitrary
string. When non- |
None
|
Raises:
| Type | Description |
|---|---|
AlmaValidationError
|
If |
Pattern source: GitHub issue #5 (HTTP: retry with exponential backoff for 429/5xx), issue #6 (HTTP: make timeout configurable; lower default from 300s to 60s), and issue #7 (HTTP: make region/host configurable).
Source code in src/almaapitk/client/AlmaAPIClient.py
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | |
get
¶
get(
endpoint: str,
params: Optional[Dict] = None,
custom_headers: Optional[Dict] = None,
) -> AlmaResponse
Make a GET request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
API endpoint (e.g., 'almaws/v1/bibs/123456') |
required |
params
|
Optional[Dict]
|
Query parameters |
None
|
custom_headers
|
Optional[Dict]
|
Additional headers |
None
|
Returns:
| Type | Description |
|---|---|
AlmaResponse
|
AlmaResponse object |
Source code in src/almaapitk/client/AlmaAPIClient.py
post
¶
post(
endpoint: str,
data: Any = None,
params: Optional[Dict] = None,
content_type: Optional[str] = None,
custom_headers: Optional[Dict] = None,
) -> Response
Make a POST request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
API endpoint |
required |
data
|
Any
|
Request body (dict for JSON, str for XML) |
None
|
params
|
Optional[Dict]
|
Query parameters |
None
|
content_type
|
Optional[str]
|
Override content type ('application/xml', etc.) |
None
|
custom_headers
|
Optional[Dict]
|
Additional headers |
None
|
Returns:
| Type | Description |
|---|---|
Response
|
AlmaResponse object |
Source code in src/almaapitk/client/AlmaAPIClient.py
put
¶
put(
endpoint: str,
data: Any = None,
params: Optional[Dict] = None,
content_type: Optional[str] = None,
custom_headers: Optional[Dict] = None,
) -> AlmaResponse
Make a PUT request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
API endpoint |
required |
data
|
Any
|
Request body (dict for JSON, str for XML) |
None
|
params
|
Optional[Dict]
|
Query parameters |
None
|
content_type
|
Optional[str]
|
Override content type ('application/xml', etc.) |
None
|
custom_headers
|
Optional[Dict]
|
Additional headers |
None
|
Returns:
| Type | Description |
|---|---|
AlmaResponse
|
AlmaResponse object |
Source code in src/almaapitk/client/AlmaAPIClient.py
delete
¶
delete(
endpoint: str,
params: Optional[Dict] = None,
custom_headers: Optional[Dict] = None,
) -> AlmaResponse
Make a DELETE request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
API endpoint |
required |
params
|
Optional[Dict]
|
Query parameters |
None
|
custom_headers
|
Optional[Dict]
|
Additional headers |
None
|
Returns:
| Type | Description |
|---|---|
AlmaResponse
|
AlmaResponse object |
Source code in src/almaapitk/client/AlmaAPIClient.py
iter_paged
¶
iter_paged(
endpoint: str,
*,
params: Optional[Dict[str, Any]] = None,
page_size: int = 100,
record_key: Optional[str] = None,
max_records: Optional[int] = None
) -> Iterator[Dict[str, Any]]
Yield records one at a time, fetching pages on demand.
Walks any Alma "list/search" endpoint that uses the standard
limit / offset pagination contract and exposes a
total_record_count field plus a record array under a
well-known key (invoice, pol, user, bib, ...).
Centralises the offset bookkeeping that previously lived inline
in every domain method that walked paged results, so callers no
longer have to re-derive the loop or remember to stop on the
total_record_count boundary.
The method is a generator: pages are fetched on demand, so a
caller that breaks out early after the first match never pays
for pages it does not need. Callers that want a list use
list(client.iter_paged(...)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
API endpoint (e.g., |
required |
params
|
Optional[Dict[str, Any]]
|
Caller-supplied query parameters. Merged with the
paginator's |
None
|
page_size
|
int
|
Records to request per page. Must be a positive
|
100
|
record_key
|
Optional[str]
|
Top-level key in the response body under which
the record array lives (e.g., |
None
|
max_records
|
Optional[int]
|
Hard cap on the number of records yielded.
|
None
|
Yields:
| Type | Description |
|---|---|
Dict[str, Any]
|
Each record dict in turn, in the order Alma returns them. |
Raises:
| Type | Description |
|---|---|
AlmaValidationError
|
If |
AlmaAPIError
|
Surfaces verbatim from the underlying
|
Pattern source: GitHub issue #11 (API: add iter_paged() generator at the client level).
Source code in src/almaapitk/client/AlmaAPIClient.py
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | |
test_connection
¶
Test if the API connection works.
Returns:
| Type | Description |
|---|---|
bool
|
True if connection successful, False otherwise |
Source code in src/almaapitk/client/AlmaAPIClient.py
switch_environment
¶
Switch between SANDBOX and PRODUCTION environments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
new_environment
|
str
|
|
required |
Raises:
| Type | Description |
|---|---|
AlmaAPIError
|
When the client has already been closed (see
:meth: |
Exception
|
Any error raised by |
Pattern source: this method mirrors the closed-state guard used
by :meth:_request (issue #13). Behaviour was previously
undefined post-close() (issue #138, finding F-012).
Source code in src/almaapitk/client/AlmaAPIClient.py
get_environment
¶
get_base_url
¶
close
¶
Close the persistent requests.Session and release pooled connections.
Idempotent: calling close more than once is safe and is a
no-op after the first call. After the session is closed, the
client transitions to a "closed" state and any subsequent HTTP
verb call (get/post/put/delete/_request) will
raise :class:AlmaAPIError. Re-creating a session implicitly on
next use was deliberately rejected: a closed client signals the
caller's intent to release the resource, and silently rebuilding
it would mask programmer errors (e.g., reusing a with-block
client outside the block).
If the caller still needs to make calls after closing, they
should construct a new AlmaAPIClient.
Note
close raises nothing. The requests.Session.close call is
best-effort: any exception while closing is swallowed and logged
at WARNING level so teardown in __exit__ (the typical caller)
never masks an in-flight exception from the with body.
Pattern source: GitHub issue #13.
Source code in src/almaapitk/client/AlmaAPIClient.py
AlmaResponse¶
The wrapper returned by most calls. Use .data / .json() for the parsed
body and .success to check the outcome.
AlmaResponse
¶
Response wrapper to maintain compatibility with existing domain classes.
The parsed JSON body is cached on first access (issue #16): .data
and .json() and the client's debug-body-logging path all share a
single response.json() call. Repeated access — common in idioms
like if r.data and r.data.get("foo"): — no longer re-parses the
body on every read, which is measurable on large analytics payloads.
Source code in src/almaapitk/client/AlmaAPIClient.py
data
property
¶
Cached parsed JSON body (alias for json()).
First access parses; subsequent accesses return the cached
object, so idioms like if r.data and r.data.get('x'): only
pay the parse cost once (issue #16). Shares its cache with
json() and the internal _safe_body() debug-logging
path -- self._response.json() is called at most once per
response across all three.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Parsed JSON body of the response. |
Raises:
| Type | Description |
|---|---|
ValueError
|
When the response body is not valid JSON. |
json
¶
Return the parsed JSON body of the response.
Cached on first successful parse; subsequent calls return the
cached value without touching self._response.json() again
(issue #16). Exception behaviour matches the pre-#16 contract
-- callers that previously got a ValueError on a malformed
body still do.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Parsed JSON body of the response. |
Raises:
| Type | Description |
|---|---|
ValueError
|
When the response body is not valid JSON. |