Fetching & clients

iiisight exposes two client facades with identical surfaces:

  • iiisight.AsyncClient — the flagship, over an httpx.AsyncClient pool.

  • iiisight.Client — the synchronous twin, no await.

Both are context managers. Everything below shows both styles.

Fetching documents

fetch() auto-detects a manifest vs. a collection; fetch_manifest() and fetch_collection() assert the type (raising UnexpectedDocumentError on a mismatch).

async with iiisight.AsyncClient() as client:
    doc = await client.fetch("https://example.org/manifest.json")
    manifest = await client.fetch_manifest("https://example.org/manifest.json")
    collection = await client.fetch_collection("https://example.org/collection.json")
with iiisight.Client() as client:
    doc = client.fetch("https://example.org/manifest.json")
    manifest = client.fetch_manifest("https://example.org/manifest.json")
    collection = client.fetch_collection("https://example.org/collection.json")

If a fetched document has no id, iiisight fills it from the final (post-redirect) URL. Transport failures — a 404, a connection error, or a non-JSON body — raise FetchError. (Document imperfection never raises; see Tolerance & diagnostics.)

Collections and expand

Network I/O is always explicit. A collection’s items are usually references; expand() resolves them one level deep, preserving order. Embedded items pass through untouched. The async version bounds concurrency.

async with iiisight.AsyncClient() as client:
    collection = await client.fetch_collection("https://example.org/collection.json")
    manifests = await client.expand(collection)   # fetch referenced manifests
    for manifest in manifests:
        print(str(manifest.label))
with iiisight.Client() as client:
    collection = client.fetch_collection("https://example.org/collection.json")
    manifests = client.expand(collection)
    for manifest in manifests:
        print(str(manifest.label))

To fetch a single reference, use resolve():

first = collection.items[0]
doc = await client.resolve(first)      # sync: client.resolve(first)

Bringing your own httpx client

Pass http_client= to reuse a configured httpx client — for custom authentication, caching, proxies, or timeouts. iiisight will not close a client you supplied.

import httpx

http = httpx.AsyncClient(headers={"Authorization": "Bearer …"}, timeout=60)
async with iiisight.AsyncClient(http_client=http) as client:
    manifest = await client.fetch_manifest(url)
# `http` is still open; close it yourself when done.
import httpx

http = httpx.Client(headers={"Authorization": "Bearer …"}, timeout=60)
with iiisight.Client(http_client=http) as client:
    manifest = client.fetch_manifest(url)

One-shot convenience

For quick scripts, module-level helpers create and close a client for you:

import asyncio, iiisight

# async
manifest = asyncio.run(iiisight.fetch_manifest(url))
collection = asyncio.run(iiisight.fetch_collection(url))

# sync
manifest = iiisight.fetch_manifest_sync(url)
collection = iiisight.fetch_collection_sync(url)

Parsing without fetching

If you already have a parsed JSON dict, normalize it directly — no client needed. This is pure and synchronous:

manifest = iiisight.normalize(document_dict)