A tolerant, async (and sync) Python client for consuming IIIF.
Project description
iiisight
Goal: a reusable Python package for building applications that consume IIIF — the read-side substrate for viewers, search, harvesting, and conformance tooling.
A tolerant, async Python client for consuming IIIF. Fetch a manifest or collection, normalize v2→v3 so you work against one model, walk canvases / ranges / annotations, and pull tile-aware image regions — behind a single typed interface.
Why
Python's IIIF tooling is real but fragmented — split by concern and by spec version, and all of it synchronous:
- iiif-prezi3 is the construction library — it builds (and can load static) Presentation v3 documents. It doesn't fetch from URLs, doesn't handle v2, isn't async, and offers no traversal or Image API helpers.
- piffle parses/builds Image API URLs and can
from_url()a manifest and iterate canvases — the closest thing to a client — but it's sync, does no v2→v3 normalization, has thin range/annotation traversal, and stops short of tile-aware region math. - iiif-prezi (v2 reference) and prezi-2-to-3 (standalone v2→v3 upgrade) cover the older version and the upgrade in isolation.
Every piece exists somewhere; no single library assembles them for a consumer. There is no async, tolerant, version-agnostic read model with tile-aware retrieval — the shape the JS ecosystem gets from @iiif/parser. That integration is the gap iiisight fills. Two of its capabilities — async fetching and tile-aware Image API retrieval — exist nowhere else in Python today.
Positioning
iiisight's identity is the read/consume side of IIIF, done well in one place. Overlapping existing tools is fine — even desirable — when consolidating a capability into the same tolerant, async client makes it better or cheaper than today's fragmented status quo. The test for pulling something in is not "does another tool already do this?" but "does iiisight's existing machinery make the unified version genuinely better, and does it stay read-shaped?" By that test an Image API conformance probe (we already fetch info.json and build region requests) and a consumer-oriented manifest lint (our normalizer already locates every deviation it repairs) are natural extensions, not scope creep.
Reuse vs. reinvent is then a pure engineering call, not deference: reuse (e.g. iiif-prezi3's v3 types, prezi-2-to-3's upgrade) when the dependency is clean and saves maintenance; own the layer when its constraints — sync, write-oriented model, low tolerance — would cap how good the unified tool can be. Consolidating "better in one place" will often favor owning more of the stack so tolerance, async, and diagnostics stay first-class.
Design pillars
- Read-first — consuming IIIF, not authoring it. Building manifests is iiif-prezi3's job.
- Async-first, sync-supported — non-blocking fetches for manifests, collections,
info.json, and tiles (httpx), with a first-class syncClientfor the many GLAM/library-tech consumers on sync stacks. Both share one pure, I/O-free core. - Tolerant — accept real-world, spec-imperfect documents; degrade gracefully instead of raising on the first surprise.
- Normalized — present one model regardless of source version; upgrade Presentation v2 → v3 under the hood.
- Ergonomic traversal — walk collections → manifests → canvases → ranges → annotations without hand-parsing JSON-LD.
- Tile-aware Image API — read a service's
info.jsonand compute correct region/size/tile requests rather than fetching full-resolution images.
IIIF API coverage
IIIF is a family of six APIs. As a consume-side client, iiisight can support all of them to varying depth — core delivery now, the rest as read-shaped layers over the same machinery:
| IIIF API | Stable | iiisight support | Tier |
|---|---|---|---|
| Presentation | 3.0 (4.0 RC) | Full — fetch, tolerant parse, v2→v3 normalize, traverse | Core |
| Image | 3.0 (+2.x) | Full — info.json parse, URL build/parse, tile-aware retrieval |
Core |
| Content Search | 2.0 (+1.0) | Issue search to the service, parse hits as annotations (reuses our model) | Extension |
| Content State | 1.0 | Decode/encode a resource/region reference, resolve → fetch | Extension |
| Change Discovery | 1.0 | Consume Activity-Streams OrderedCollection for harvesting/sync |
Adjacent |
| Auth Flow | 2.0 (+1.0) | Partial — model the auth services + drive a non-interactive token flow; interactive login stays the host app's job | Later |
Version handling: Presentation 2.0/2.1 → normalized to 3.0; the 4.0 release candidate informs forward-compatible modeling but 3.0 is the normalization target until 4.0 is stable. Image API 2.x and 3.0 URL/info.json syntaxes are both understood.
Two honest boundaries: Auth is inherently partial for a library — the interactive login step needs a browser/UI only the host app can own. And server sides stay out — iiisight is a client of Image / Search / Auth endpoints, never an implementation of one.
Validation & conformance
Overlap here is welcome, built as optional layers over the same client once the core read path is solid — not as separate tools:
- Image API conformance probe — the natural fit, because Image API validation is a client making requests to a service, which iiisight's tile-aware layer already does. A strict "probe this endpoint against its claimed compliance level" mode reuses the fetch machinery directly.
- Consumer-oriented manifest lint — a byproduct of tolerance: the normalizer records structured diagnostics (missing field → assumed default, wrong
type→ coerced, v2 → upgraded, bad URL → skipped) as it repairs a document, so it can report "what a real client had to guess or fix to read this." This is client-compatibility feedback, distinct from (and complementary to) strict spec conformance.
The client core stays tolerant; validation is a separate reporting posture over the same parse, never a stricter default that would make the client reject real-world documents.
Non-goals
The one deliberate exclusion is authoring — building and serializing new manifests is a different posture (write vs. read) and audience; iiif-prezi3 owns it and iiisight stays a consumer. iiisight is likewise a client of Image / Search / Auth endpoints, never a server implementation of any of them.
Distribution
iiisight is a pure-Python library published to PyPI — no compiled extensions, so it installs as a single universal wheel everywhere Python runs:
pip install iiisight
- Library-first, not an application or service: you
import iiisightand bring your own event loop; there is no server or daemon. - Lean footprint — one runtime dependency (
httpx). Optional features live behind extras, e.g.iiisight[prezi3](iiif-prezi3 interop). - Built-in CLI — installing the package registers the
iiisightcommand (stdlib-only, no extra needed):iiisight lint <url>,iiisight info <url>,iiisight tiles <url>. - Releases are git-tag driven (setuptools-scm →
python -m build→ OIDC trusted publishing); seeCLAUDE.md.
Usage
Full documentation — installation, a quickstart, task-oriented guides, and the API
reference — lives on Read the Docs. A tour
of the main workflows follows; everything is available in both async (flagship) and
sync forms — AsyncClient / Client mirror httpx's naming, and every
await client.… has a no-await twin.
Fetch and traverse
import iiisight
async with iiisight.AsyncClient() as client:
manifest = await client.fetch_manifest("https://example.org/manifest.json")
print(str(manifest.label)) # LanguageMap -> display string
for canvas in manifest.canvases:
print(canvas.width, canvas.height, [img.id for img in canvas.images])
for entry in manifest.structures: # ranges / table of contents
print(str(entry.label))
# sync equivalent — same methods, no await
with iiisight.Client() as client:
manifest = client.fetch_manifest("https://example.org/manifest.json")
v2 and v3 manifests normalize to the same model; a v2 document is upgraded under
the hood. Network I/O is always explicit — expand() resolves a collection's
references, load_info() fetches an image service's info.json:
collection = await client.fetch_collection("https://example.org/collection.json")
manifests = await client.expand(collection) # fetch referenced manifests
Tile-aware Image API retrieval
service = await client.load_info(manifest.canvases[0]) # fetch info.json
service.url(region="full", size="max") # spec-correct Image API URL
service.thumbnail_url(width=400) # prefers an advertised size
for tile in service.tile_grid(scale_factor=4): # a full tile grid at a zoom
print(tile.region, tile.size, tile.url)
data = await client.fetch_region(service, region="0,0,512,512", size="256,")
Content Search
page = await client.search(manifest, "cathedral") # discovers the search service
for hit in page.items:
print(hit.target, hit.body)
Content State
state = iiisight.content_state.decode(token) # bare URI, JSON, or base64url
doc = await client.resolve(state.reference) # fetch what it points at
token = iiisight.content_state.encode("https://example.org/canvas/1", region="xywh=0,0,50,50")
Auth Flow 2.0 (metadata + non-interactive)
probe = iiisight.find_probe_service(manifest.canvases[0])
if probe: # resource is access-controlled
result = await client.probe(probe) # check access
if not result.accessible and probe.token_service:
token = await client.access_token(probe.token_service) # non-interactive only
result = await client.probe(probe, token=token.access_token)
Interactive browser login is the host application's job; iiisight supplies the service metadata to drive it.
Lint
report = iiisight.lint(manifest) # what a real client had to repair
print(report.summary()) # "1 error(s), 2 warning(s), 3 info"
for problem in report.errors + report.warnings:
print(problem.severity, problem.code, problem.path, problem.message)
Command line
Installing the package registers the iiisight command (a URL or local JSON file
works for each):
$ iiisight lint https://example.org/manifest.json # exits non-zero on errors
$ iiisight info manifest.json # summarize a document
$ iiisight tiles https://example.org/manifest.json --scale 4
Status
Working, pre-1.0 (see PLAN.md for phase-by-phase status). Done: the tolerant
normalizer + typed model, the v2→v3 upgrade, async/sync fetch clients, tile-aware
Image API retrieval (validated against live servers), Content Search, Content
State, Auth Flow 2.0, and the consumer lint. Still to come: the Image API
conformance probe, Change Discovery, an optional CLI, and the first PyPI release
(the name iiisight is available). The public API may still change before 1.0.
License
Project details
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 iiisight-0.2.tar.gz.
File metadata
- Download URL: iiisight-0.2.tar.gz
- Upload date:
- Size: 93.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ae44d2331117bff72dbf619e1b4556e21c252f8b598d31d338d57e8f86e23f9
|
|
| MD5 |
99bf6ab969da411210d0610ab122e6d3
|
|
| BLAKE2b-256 |
18b1f6be0527e582f222a6a1e07858e53f949b87a5bb51b6ca7d8feb1ff4ef21
|
File details
Details for the file iiisight-0.2-py3-none-any.whl.
File metadata
- Download URL: iiisight-0.2-py3-none-any.whl
- Upload date:
- Size: 42.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2002df29496f39bf8711958c966cdf3f9f87d07a2735db2f82063622a23990d
|
|
| MD5 |
00b0675d177bccc344adc3aa1d93eb8a
|
|
| BLAKE2b-256 |
eef20b107777ea8908a7384f6cc6c1cc87ac2f7d91284aa2d5c2009435c0d105
|