Making Arbitrary API Calls#
Use this page when you need a LabArchives endpoint that does not yet have a
high-level wrapper in labapi. The examples below assume you already have an
authenticated user object; see Your First Entry or Authentication if you
need to establish a session first.
Choose a Call Path#
Need |
Use |
Returns |
|---|---|---|
Signed request with |
|
Parsed XML as an |
Raw response headers and body |
|
|
Streaming large responses |
|
|
A signed URL for another tool |
|
A URL string |
Use the User Object#
For XML endpoints that need the current user ID, call
api_get() or
api_post() on User. These
methods automatically include your User ID (uid) and handle request
signing.
xml_response = user.api_get("notebooks/notebook_info", nbid="12345.6")
print(xml_response.tag)
Parsing XML Responses#
For XML API responses, labapi uses lxml for parsing.
You can work with the returned element directly or use
extract_etree() for structured extraction.
Using extract_etree#
from labapi.util import extract_etree, to_bool
format_dict = {
"notebook": {
"name": str,
"id": str,
"is-student": to_bool,
"signing": str,
}
}
data = extract_etree(xml_response, format_dict)
print(f"Notebook Name: {data['name']}")
print(f"Is Student: {data['is-student']}")
By default a missing element, or a value a mapper rejects, raises
ExtractionError. For responses where fields are
genuinely optional, pass raise_missing=False to omit them from the result
instead, and read them with dict.get():
data = extract_etree(
xml_response,
{"caption": str, "attach-file-name": str},
raise_missing=False,
)
filename = data.get("attach-file-name")
Optional extraction omits a missing element silently. A value that is present
but that the mapper rejects is also omitted, but raises a
RuntimeWarning first, since a value the API did send and that will not
parse is an anomaly rather than a routine absence.
Raw and Streaming Responses#
When you need response metadata, raw bytes, or streaming, use the lower-level
Client methods directly.
Note
Every request a client makes is bounded by the timeout given to
Client, in seconds, defaulting to 60. Long
streaming downloads are the usual reason to raise it:
client = Client(timeout=300)
Raw Responses#
If you need HTTP headers or the raw response body, use
raw_api_get() or
raw_api_post():
response = user.client.raw_api_get("users/max_file_size", uid=user.id)
print(response.status_code)
print(response.headers["Content-Type"])
Streaming Data#
For large attachments or incremental processing, use the streaming methods.
They return a StreamingResponse, which is both
iterable and a context manager:
with (
user.client.stream_api_get(
"entries/entry_attachment",
uid=user.id,
eid="987.6",
) as stream,
open("large_file.zip", "wb") as f,
):
for chunk in stream:
f.write(chunk)
Constructing Signed URLs#
Use construct_url() when code outside labapi
needs to make the signed request, such as a browser download or a
service-to-service handoff.
from datetime import timedelta
url = user.client.construct_url(
"entries/entry_attachment",
query={"uid": user.id, "eid": "987.6"},
expires_in=timedelta(minutes=10),
)
print(url)