Skip to main content

A tolerant, async (and sync) Python client for consuming IIIF.

Project description

iiisight

PyPI version Python versions License: Apache-2.0 Pipeline status Documentation

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 sync Client for 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.json and 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 iiisight and 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 iiisight command (stdlib-only, no extra needed): iiisight lint <url>, iiisight info <url>, iiisight tiles <url>, iiisight probe <image-service>, iiisight changes <url>.
  • Releases are git-tag driven (setuptools-scm → python -m build → OIDC trusted publishing); see CLAUDE.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
$ iiisight probe https://example.org/iiif/image        # Image API conformance
$ iiisight changes https://example.org/activity/all-changes   # what changed

Status

Released on PyPI (pip install iiisight), pre-1.0 (see PLAN.md for phase-by-phase status). All six IIIF APIs are covered: Presentation (v2+v3), Image (tile-aware), Content Search, Content State, Auth Flow 2.0 (metadata + non-interactive), and Change Discovery — plus a tolerant normalizer with diagnostics, a consumer lint, an Image API conformance probe, and a built-in CLI. The public API may still change before 1.0.

License

Apache-2.0 — see LICENSE and NOTICE.

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

iiisight-0.4.tar.gz (108.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

iiisight-0.4-py3-none-any.whl (49.5 kB view details)

Uploaded Python 3

File details

Details for the file iiisight-0.4.tar.gz.

File metadata

  • Download URL: iiisight-0.4.tar.gz
  • Upload date:
  • Size: 108.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for iiisight-0.4.tar.gz
Algorithm Hash digest
SHA256 a28dacf9209410ff61bb7ffc510452a30f9f87ab17e07b7d8813cfab15176906
MD5 5f2e8974b1e2175ad197c3ee112c757a
BLAKE2b-256 da8ab4f17d0ad3caa2ce729e6962a245615f9f078cfb64dc6f3d918dc391e3bb

See more details on using hashes here.

File details

Details for the file iiisight-0.4-py3-none-any.whl.

File metadata

  • Download URL: iiisight-0.4-py3-none-any.whl
  • Upload date:
  • Size: 49.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for iiisight-0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 1bcd8be978bd002d9b8b08ee6add2aae08d97517606d78b011efa5218a944089
MD5 3c1ee063cfbd37a83e8b6c85600b31f0
BLAKE2b-256 63f3d7965d696188a7e7f2903ec61376049dad07202ab71138583d482d89cd84

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page