SDK API Reference¶
Auto-generated from source code docstrings.
rahcp-client¶
HCPClient¶
HCPClient(endpoint='http://localhost:8000/api/v1', username='', password='', tenant=None, *, timeout=30.0, max_retries=4, retry_base_delay=1.0, multipart_threshold=100 * 1024 * 1024, multipart_chunk=64 * 1024 * 1024, multipart_concurrency=6, verify_ssl=True)
¶
Async HTTP client for the HCP Unified API.
Usage::
async with HCPClient.from_env() as hcp:
buckets = await hcp.s3.list_objects("bucket", "prefix/")
ns = await hcp.mapi.list_namespaces("tenant")
Initialize the HCP client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint
|
str
|
Base URL for the HCP Unified API. |
'http://localhost:8000/api/v1'
|
username
|
str
|
Username for authentication. |
''
|
password
|
str
|
Password for authentication. |
''
|
tenant
|
str | None
|
HCP tenant name (routes requests to the correct tenant). |
None
|
timeout
|
float
|
HTTP request timeout in seconds. |
30.0
|
max_retries
|
int
|
Maximum number of retry attempts for transient errors. |
4
|
retry_base_delay
|
float
|
Base delay between retries in seconds (doubles each attempt). |
1.0
|
multipart_threshold
|
int
|
File size in bytes above which multipart upload is used. |
100 * 1024 * 1024
|
multipart_chunk
|
int
|
Part size in bytes for multipart uploads. |
64 * 1024 * 1024
|
verify_ssl
|
bool
|
Whether to verify TLS certificates. |
True
|
Source code in packages/rahcp-client/src/rahcp_client/client.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
token
property
¶
The current bearer token (set after login).
s3
property
¶
S3 data-plane operations.
mapi
property
¶
MAPI management-plane operations.
transfer_settings
property
¶
Settings needed by the bulk transfer engine.
from_env()
classmethod
¶
Create a client configured from HCP_* environment variables.
Source code in packages/rahcp-client/src/rahcp_client/client.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
request(method, path, *, params=None, json=None, data=None, content=None, headers=None)
async
¶
Send an HTTP request with retry, tracing, and automatic 401 refresh.
Source code in packages/rahcp-client/src/rahcp_client/client.py
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
S3Ops¶
S3Ops(client)
¶
S3 data-plane operations — presigned-first, multipart for large files.
All data transfer prefers presigned URLs. Files above
client.multipart_threshold automatically use multipart upload.
Path mapping to the backend API (all under /api/v1): GET /buckets — list buckets GET /buckets/{b}/objects — list objects POST /presign — single presigned URL POST /buckets/{b}/objects/presign — bulk presigned URLs POST /buckets/{b}/multipart/{key} — initiate multipart POST /buckets/{b}/multipart/{key}/presign — presign parts POST /buckets/{b}/multipart/{key}/complete — complete multipart DELETE /buckets/{b}/objects/{key} — delete object POST /buckets/{b}/objects/delete — bulk delete POST /buckets/{b}/objects/{key}/copy — copy object HEAD /buckets/{b}/objects/{key} — head object
Source code in packages/rahcp-client/src/rahcp_client/s3.py
52 53 | |
presign_get(bucket, key, *, expires=3600)
async
¶
Get a presigned download URL.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
63 64 65 66 67 68 69 70 71 72 73 74 75 | |
presign_put(bucket, key, *, expires=3600)
async
¶
Get a presigned upload URL.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
77 78 79 80 81 82 83 84 85 86 87 88 89 | |
presign_bulk(bucket, keys, *, method='get_object', expires=3600)
async
¶
Presign multiple keys in one API call. Returns {key: url} mapping.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
upload(bucket, key, data)
async
¶
Upload data — auto-selects presigned PUT or multipart based on size.
Returns the ETag of the uploaded object.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | |
download(bucket, key, dest)
async
¶
Download an object via presigned GET. Returns byte count.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
download_bytes(bucket, key)
async
¶
Download an object as bytes via presigned GET.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
upload_multipart(bucket, key, path, *, concurrency=None)
async
¶
Presigned multipart upload for large files.
- Initiate multipart → upload_id
- Presign each part
- Upload parts in parallel (bounded semaphore)
- Complete multipart (or abort on failure)
Source code in packages/rahcp-client/src/rahcp_client/s3.py
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | |
list_buckets()
async
¶
List all S3 buckets.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
320 321 322 323 | |
list_objects(bucket, prefix='', *, max_keys=1000, continuation_token=None, delimiter=None)
async
¶
List objects in a bucket with optional prefix and pagination.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | |
delete(bucket, key)
async
¶
Delete a single object.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
364 365 366 | |
delete_bulk(bucket, keys)
async
¶
Delete multiple objects.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
368 369 370 371 372 373 374 375 | |
copy(bucket, key, source_bucket, source_key)
async
¶
Copy an object.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | |
head(bucket, key)
async
¶
Get object metadata (content-length, content-type, etag, last-modified).
Source code in packages/rahcp-client/src/rahcp_client/s3.py
394 395 396 397 | |
commit_staging(bucket, staging_prefix, dest_prefix)
async
¶
Move objects from staging prefix to destination. Returns count.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
401 402 403 404 405 406 407 408 409 410 411 412 | |
cleanup_staging(bucket, staging_prefix)
async
¶
Delete all objects under a staging prefix. Paginates. Returns count.
Source code in packages/rahcp-client/src/rahcp_client/s3.py
414 415 416 417 418 419 420 421 422 423 424 425 426 427 | |
MapiOps¶
MapiOps(client)
¶
MAPI management-plane — tenant admin and namespace operations only.
This is intentionally thin: user/group CRUD, statistics, chargeback, and system-level admin are handled by the backend directly.
HTTP method mapping (matches HCP MAPI conventions): GET — read PUT — create POST — update/modify DELETE — delete
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
27 28 | |
list_namespaces(tenant, *, verbose=False)
async
¶
List all namespaces for a tenant.
Returns the MAPI response shape {"name": ["ns1", "ns2", ...]}.
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
get_namespace(tenant, ns, *, verbose=False)
async
¶
Get namespace details.
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
45 46 47 48 49 50 51 52 53 54 55 | |
create_namespace(tenant, ns_data)
async
¶
Create a namespace. Uses PUT (MAPI convention).
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
57 58 59 60 61 62 63 64 | |
update_namespace(tenant, ns, data)
async
¶
Update namespace settings. Uses POST (MAPI convention).
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
66 67 68 69 70 71 72 | |
delete_namespace(tenant, ns)
async
¶
Delete a namespace.
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
74 75 76 | |
export_namespace(tenant, ns)
async
¶
Export a namespace as a reusable template.
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
78 79 80 81 82 83 | |
export_namespaces(tenant, names)
async
¶
Export multiple namespaces as templates.
Source code in packages/rahcp-client/src/rahcp_client/mapi.py
85 86 87 88 89 90 91 92 | |
Configuration¶
HCPSettings
¶
Bases: BaseSettings
Configuration for the HCP client.
All values can be set via HCP_-prefixed environment variables,
e.g. HCP_ENDPOINT, HCP_USERNAME, HCP_TENANT.
Bulk Transfers¶
config
¶
Configuration and stats models for bulk transfers.
ConflictPolicy
¶
Bases: StrEnum
What to do when an upload's destination key already exists in the bucket.
overwrite— upload unconditionally, replacing the remote object (no pre-flight HEAD).skip— keep the remote object and count the item as skipped; a size mismatch between local and remote is logged as a warning.error— keep the remote object and record a conflict error for the item (visible in the tracker and error callbacks).
TransferStats
¶
BulkStreamConfig
¶
Bases: BaseModel
Configuration for a streaming upload job (bytes fetched on the fly).
Like BulkUploadConfig but with no source_dir — each object's bytes
come from a caller-supplied fetch callable instead of a local file, so
nothing is staged to disk. Validation is byte-based (validate_bytes).
conflict_policy()
¶
Effective policy — explicit on_conflict wins over legacy skip_existing.
Source code in packages/rahcp-client/src/rahcp_client/bulk/config.py
89 90 91 92 93 | |
BulkUploadConfig
¶
Bases: BaseModel
Configuration for a bulk upload job.
conflict_policy()
¶
Effective policy — explicit on_conflict wins over legacy skip_existing.
Source code in packages/rahcp-client/src/rahcp_client/bulk/config.py
121 122 123 124 125 | |
BulkDownloadConfig
¶
Bases: BaseModel
Configuration for a bulk download job.
upload
¶
Bulk upload — producer-consumer pipeline with batch presigning.
bulk_upload(cfg)
async
¶
Upload a directory to S3 with batch presigning and connection pooling.
Source code in packages/rahcp-client/src/rahcp_client/bulk/upload.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
download
¶
Bulk download — producer-consumer pipeline with batch presigning.
bulk_download(cfg)
async
¶
Download objects from S3 with batch presigning and connection pooling.
Source code in packages/rahcp-client/src/rahcp_client/bulk/download.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
stream
¶
Streaming bulk upload — fetch each object's bytes on the fly, never staged to disk.
Shares the producer/consumer pipeline, batched presigning, HEAD skip/verify guards,
and tracker bookkeeping with bulk_upload; only the source differs (a caller
fetch coroutine returning bytes instead of a local file).
bulk_stream_upload(cfg, items, fetch)
async
¶
Upload objects whose bytes are fetched on demand (no local staging).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
BulkStreamConfig
|
Streaming upload configuration. |
required |
items
|
list[tuple[str, str]]
|
|
required |
fetch
|
Callable[[str], Awaitable[bytes]]
|
Async callable |
required |
Per item (unless already done / already in the bucket): skip-HEAD →
fetch → optional byte validation → presigned PUT → optional verify →
tracker mark. Reuses the same engine and guards as bulk_upload.
Source code in packages/rahcp-client/src/rahcp_client/bulk/stream.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | |
Errors¶
errors
¶
HCPError hierarchy — maps HTTP status codes to typed exceptions.
HCPError(message, *, status_code=None)
¶
Bases: Exception
Base error for all rahcp operations.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
AuthenticationError(message, *, status_code=None)
¶
Bases: HCPError
401 — bad credentials or expired token.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
NotFoundError(message, *, status_code=None)
¶
Bases: HCPError
404 — tenant, namespace, or object not found.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
ConflictError(message, *, status_code=None)
¶
Bases: HCPError
409 — resource already exists.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
RetryableError(message, *, status_code=None)
¶
Bases: HCPError
408, 429, 500, 502, 503, 504 — transient failure after all retries exhausted.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
UpstreamError(message, *, status_code=None)
¶
Bases: RetryableError
502 — backend gateway/upstream error. Transient: retried, then raised.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
9 10 11 12 | |
error_for_status(status_code, message)
¶
Map an HTTP status code to the appropriate HCPError subclass.
Source code in packages/rahcp-client/src/rahcp_client/errors.py
40 41 42 43 | |
Tracing¶
tracing
¶
Optional OpenTelemetry tracing — no-op when OTEL is not installed.
Automatically initializes the OTEL SDK when OTEL_EXPORTER_OTLP_ENDPOINT
is set (or when configure_tracing() is called explicitly).
Supports both grpc and http/protobuf protocols via the standard
OTEL_EXPORTER_OTLP_PROTOCOL env var.
Usage::
from rahcp_client.tracing import tracer
with tracer.start_as_current_span("s3.upload") as span:
span.set_attribute("s3.bucket", bucket)
...
If opentelemetry-api is not installed, tracer is a no-op.
configure_tracing(service_name='rahcp-client', endpoint=None, protocol=None)
¶
Initialize the OTEL SDK with OTLP exporter.
Called automatically if OTEL_EXPORTER_OTLP_ENDPOINT env var is set.
Safe to call multiple times — only initializes once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service_name
|
str
|
Service name in traces (default: rahcp-client). |
'rahcp-client'
|
endpoint
|
str | None
|
OTLP endpoint. Defaults to |
None
|
protocol
|
str | None
|
|
None
|
Source code in packages/rahcp-client/src/rahcp_client/tracing.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
rahcp-tracker¶
Tracker Protocol¶
protocol
¶
TrackerProtocol — the interface all tracker backends must implement.
TrackerProtocol
¶
Bases: Protocol
Interface for transfer state tracking.
Any object implementing these methods can be used as a tracker
in bulk transfer operations. The default implementation is
:class:~rahcp_tracker.sqlite.SqliteTracker.
delete(key)
¶
Flush the buffer, then delete the entry for key if present.
Source code in packages/rahcp-tracker/src/rahcp_tracker/protocol.py
25 26 27 | |
SQLite Tracker¶
sqlite
¶
SqliteTracker — thread-safe, buffered transfer tracker backed by SQLite.
SqliteTracker(db_path, *, flush_every=200)
¶
Bases: _BufferedSqlTracker
Thread-safe, buffered transfer tracker backed by SQLite.
Marks are buffered in memory and flushed to the database either
explicitly via flush() or automatically when the buffer reaches
flush_every entries.
Usage::
tracker = SqliteTracker(Path(".upload-tracker.db"))
tracker.mark("folder/file.jpg", 12345, TransferStatus.done, etag='"abc123"')
tracker.flush()
done = tracker.done_keys()
tracker.close()
Source code in packages/rahcp-tracker/src/rahcp_tracker/sqlite.py
35 36 37 38 39 40 41 42 43 44 45 46 | |
PostgreSQL Tracker¶
postgres
¶
PostgresTracker — thread-safe, buffered transfer tracker backed by PostgreSQL.
PostgresTracker(dsn, *, flush_every=200)
¶
Bases: _BufferedSqlTracker
Thread-safe, buffered transfer tracker backed by PostgreSQL.
Same behavior and interface as :class:~rahcp_tracker.sqlite.SqliteTracker,
but connects to a PostgreSQL server instead of a local file — use it when
several machines share one tracker, or tracker state must outlive the host.
Requires the postgres extra: pip install "rahcp-tracker[postgres]".
Usage::
tracker = PostgresTracker("postgresql://user:pass@host:5432/rahcp")
tracker.mark("folder/file.jpg", 12345, TransferStatus.done)
tracker.close()
Source code in packages/rahcp-tracker/src/rahcp_tracker/postgres.py
35 36 37 38 39 40 41 42 43 | |
Backend Selection¶
factory
¶
Select a tracker backend from a SQLite file path or a PostgreSQL DSN.
redact_dsn(dsn)
¶
Render a DSN with the password masked — safe for console output and logs.
Source code in packages/rahcp-tracker/src/rahcp_tracker/factory.py
18 19 20 | |
create_tracker(target, *, flush_every=200)
¶
Create a tracker from target.
Targets with a PostgreSQL scheme (postgres://, postgresql://,
postgresql+<driver>://) select :class:PostgresTracker; any other
URL scheme is rejected loudly; everything else is treated as a SQLite
database file path.
Source code in packages/rahcp-tracker/src/rahcp_tracker/factory.py
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
rahcp-iiif¶
Manifest Helpers¶
manifest
¶
IIIF manifest parsing — extract image IDs from Riksarkivet manifests.
fetch_with_retry(client, url, *, attempts=4, base_delay=0.5)
async
¶
GET url, retrying transient failures with exponential backoff + jitter.
Retries connection/timeout errors and 408/425/429/5xx responses. Any other 4xx (e.g. 404) is treated as terminal and raised immediately — retrying it would only waste time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
AsyncClient
|
An open |
required |
url
|
str
|
The URL to GET. |
required |
attempts
|
int
|
Maximum number of attempts (1 disables retrying). |
4
|
base_delay
|
float
|
Base backoff delay in seconds; also bounds the jitter, so
|
0.5
|
Source code in packages/rahcp-iiif/src/rahcp_iiif/manifest.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
get_image_ids(batch_id, *, base_url=IIIF_URL, timeout=IIIF_TIMEOUT, attempts=4, base_delay=0.5, headers=None)
async
¶
Fetch a IIIF manifest and extract image IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_id
|
str
|
Volume/batch identifier (e.g. "C0074667"). |
required |
base_url
|
str
|
IIIF server base URL. |
IIIF_URL
|
timeout
|
float
|
HTTP request timeout in seconds. |
IIIF_TIMEOUT
|
attempts
|
int
|
Maximum manifest-fetch attempts (transient failures retried). |
4
|
base_delay
|
float
|
Base backoff delay in seconds between retries. |
0.5
|
headers
|
dict[str, str] | None
|
Extra HTTP headers sent with every request (e.g. Referer). |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Sorted list of image IDs (e.g. ["C0074667_00001", "C0074667_00002", ...]). |
Source code in packages/rahcp-iiif/src/rahcp_iiif/manifest.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
build_image_url(image_id, *, base_url=IIIF_URL, query_params=IIIF_QUERY_PARAMS)
¶
Build the IIIF image URL for a given image ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image_id
|
str
|
Image identifier (e.g. "C0074667_00001"). |
required |
base_url
|
str
|
IIIF server base URL. |
IIIF_URL
|
query_params
|
str
|
IIIF image API parameters (region/size/rotation/quality.format). |
IIIF_QUERY_PARAMS
|
Returns:
| Type | Description |
|---|---|
str
|
Full image URL. |
Source code in packages/rahcp-iiif/src/rahcp_iiif/manifest.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
file_extension(query_params=IIIF_QUERY_PARAMS)
¶
Extract file extension from IIIF query params (e.g. '.jpg').
Source code in packages/rahcp-iiif/src/rahcp_iiif/manifest.py
123 124 125 | |
Downloader¶
downloader
¶
Async bulk IIIF image downloader with tracker-based resumability.
DownloadStats()
¶
Running counters for a bulk IIIF download.
Source code in packages/rahcp-iiif/src/rahcp_iiif/downloader.py
39 40 41 42 43 44 | |
download_batch(batch_id, output_dir, tracker, *, base_url=IIIF_URL, query_params=IIIF_QUERY_PARAMS, timeout=IIIF_TIMEOUT, headers=None, workers=4, max_images=None, validate_file=None, max_attempts=4, retry_base_delay=0.5, on_progress=None, on_error=None, progress_interval=5.0)
async
¶
Download all images from a IIIF batch to output_dir with parallel workers.
For streaming images straight to S3 without staging to disk, use
rahcp_client.bulk.bulk_stream_upload (the rahcp iiif upload command).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_id
|
str
|
Volume/batch identifier (e.g. "C0074667"). |
required |
output_dir
|
Path
|
Local directory to save images into. |
required |
tracker
|
TrackerProtocol
|
Transfer tracker for resumability. |
required |
base_url
|
str
|
IIIF server base URL. |
IIIF_URL
|
query_params
|
str
|
IIIF image API parameters (region/size/rotation/quality.format). |
IIIF_QUERY_PARAMS
|
timeout
|
float
|
HTTP request timeout per image. |
IIIF_TIMEOUT
|
headers
|
dict[str, str] | None
|
Extra HTTP headers sent with every request (e.g. Referer). |
None
|
workers
|
int
|
Number of concurrent download workers. |
4
|
max_images
|
int | None
|
Limit number of images to download (None = all). |
None
|
validate_file
|
Callable[[Path], None] | None
|
Optional callback to validate each downloaded file. |
None
|
max_attempts
|
int
|
Max attempts per network fetch (manifest and each image). Transient failures (connection/timeout, 408/429/5xx) are retried; terminal ones (e.g. 404) are not. 1 disables retrying. |
4
|
retry_base_delay
|
float
|
Base backoff delay in seconds between retries. |
0.5
|
on_progress
|
Callable[[DownloadStats], None] | None
|
Optional callback for periodic progress updates. |
None
|
on_error
|
Callable[[str, Exception], None] | None
|
Optional callback for per-file errors. |
None
|
progress_interval
|
float
|
Minimum seconds between progress callbacks. |
5.0
|
Returns:
| Type | Description |
|---|---|
DownloadStats
|
Download statistics. |
Source code in packages/rahcp-iiif/src/rahcp_iiif/downloader.py
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
download_batches(batch_ids, output_dir, tracker, **kwargs)
async
¶
Download multiple batches sequentially, sharing one tracker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_ids
|
list[str]
|
List of batch identifiers. |
required |
output_dir
|
Path
|
Root output directory. |
required |
tracker
|
TrackerProtocol
|
Shared transfer tracker. |
required |
**kwargs
|
Forwarded to :func: |
{}
|
Returns:
| Type | Description |
|---|---|
DownloadStats
|
Aggregated download statistics. |
Source code in packages/rahcp-iiif/src/rahcp_iiif/downloader.py
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | |
rahcp-transkribus¶
Client¶
client
¶
Minimal async Transkribus TRP REST client (httpx + tenacity).
Reimplements just the handful of read calls an export needs — login, list
documents, list pages, fetch transcript/image bytes — against the Transkribus
REST API. Auth is a JSESSIONID cookie obtained from /auth/login.
TranskribusClient(username, password, *, base_url=TRANSKRIBUS_URL, timeout=TRANSKRIBUS_TIMEOUT, verify_ssl=None, max_attempts=4, retry_base_delay=0.5)
¶
Async client for the Transkribus TRP REST API.
Use as an async context manager — it logs in on entry and out on exit::
async with TranskribusClient(user, pw) as client:
docs = await client.list_docs(collection_id)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
username
|
str
|
Transkribus login (email). |
required |
password
|
str
|
Transkribus password. |
required |
base_url
|
str
|
TRP REST base URL. |
TRANSKRIBUS_URL
|
timeout
|
float
|
Per-request timeout in seconds. |
TRANSKRIBUS_TIMEOUT
|
verify_ssl
|
bool | None
|
TLS verification; |
None
|
max_attempts
|
int
|
Max attempts per request (transient failures retried). |
4
|
retry_base_delay
|
float
|
Base backoff delay in seconds between retries. |
0.5
|
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/client.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
base_url
property
¶
The TRP REST base URL (no trailing slash).
session_id
property
¶
The active JSESSIONID, or None before login.
list_docs(collection_id)
async
¶
List all documents in a collection.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/client.py
194 195 196 197 198 199 200 201 | |
get_pages(collection_id, doc_id, *, status=None, skip_pages_with_missing_status=False, pages=None)
async
¶
List pages of a document, filtered server-side by transcript status.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
collection_id
|
int
|
Collection ID. |
required |
doc_id
|
int
|
Document ID. |
required |
status
|
str | None
|
Transcript status filter (e.g. |
None
|
skip_pages_with_missing_status
|
bool
|
Omit pages that have no transcript
with |
False
|
pages
|
str | None
|
Optional page-range string (e.g. |
None
|
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/client.py
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
get_transcript_text(collection_id, doc_id, page_nr)
async
¶
Fetch the current PAGE-XML transcript for a page via the /text endpoint.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/client.py
235 236 237 238 239 240 241 242 | |
fetch_bytes(url)
async
¶
GET raw bytes from a URL (absolute or base-relative).
Used for page images and transcript file-store URLs. The session cookie rides along automatically for same-host (REST) URLs and is harmlessly ignored by the file store.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/client.py
244 245 246 247 248 249 250 251 252 | |
Exporter¶
exporter
¶
Plan and run Transkribus collection exports (local dir or, via the CLI, a bucket).
plan_collection_export walks a collection into a flat list of
:class:ExportItem (destination key + source URL). export_collection runs
that plan to a local directory with parallel, tracker-resumable workers. The
bucket path lives in the CLI, which feeds the same items to
rahcp_client.bulk.bulk_stream_upload — this package stays HCP-agnostic.
ExportFormat
¶
Bases: StrEnum
Transcript output format.
sanitize_filename(title)
¶
Make a document title safe for use as a path segment.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
43 44 45 46 | |
plan_collection_export(client, collection_id, *, status='GT', fmt=ExportFormat.page, skip_missing=True, include_images=True, doc_ids=None)
async
¶
Resolve a collection into a flat list of export items.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
TranskribusClient
|
An entered :class: |
required |
collection_id
|
int
|
Collection to export. |
required |
status
|
str | None
|
Transcript status filter (e.g. |
'GT'
|
fmt
|
ExportFormat
|
Transcript output format ( |
page
|
skip_missing
|
bool
|
Skip pages with no transcript at |
True
|
include_images
|
bool
|
Also export each page's image. |
True
|
doc_ids
|
Iterable[int] | None
|
Restrict to these document IDs ( |
None
|
Returns:
| Type | Description |
|---|---|
list[ExportItem]
|
Export items across all matching documents. Keys are relative |
list[ExportItem]
|
( |
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
fetch_item_bytes(client, item)
async
¶
Fetch an item's bytes, converting PAGE→ALTO when the item requests it.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
167 168 169 170 171 172 | |
compute_stale_keys(items, synced, *, prefix='')
¶
Return the tracker keys of transcripts whose version changed since last sync.
A transcript is stale when it was synced before (synced has its key) but
its current ts_id differs — i.e. it was re-corrected in Transkribus. New
transcripts (never synced) are not stale; the normal done-keys logic uploads
them. Images carry no ts_id and are never stale.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | |
transcript_versions(items, done_keys, *, prefix='')
¶
Map {tracker_key: ts_id} for every transcript that is now done.
Used to update the version store after a run so the next run can detect the next round of corrections.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |
refresh_stale_transcripts(items, tracker, store, done_keys, *, prefix='')
async
¶
Drop changed transcripts from the tracker so a re-run re-fetches them.
Compares each transcript's current ts_id to the version store, deletes the
stale keys from tracker and the in-memory done_keys set, and returns
the stale keys. Call before running the transfer; pair with an overwrite
conflict policy so the bucket object is actually replaced.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |
export_collection(client, collection_id, output_dir, tracker, *, status='GT', fmt=ExportFormat.page, skip_missing=True, include_images=True, prefix='', workers=8, doc_ids=None, validate_file=None, version_db=None, on_progress=None, on_error=None, progress_interval=5.0)
async
¶
Export a Transkribus collection to a local directory, resumably.
Files already recorded done in tracker are skipped instantly.
Each item is fetched (transcripts optionally converted to ALTO), written
under output_dir/<prefix><key>, and marked in the tracker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
TranskribusClient
|
An entered :class: |
required |
collection_id
|
int
|
Collection to export. |
required |
output_dir
|
Path
|
Local root directory to write into. |
required |
tracker
|
TrackerProtocol
|
Transfer tracker for resumability. |
required |
status
|
str | None
|
Transcript status filter (e.g. |
'GT'
|
fmt
|
ExportFormat
|
Transcript output format ( |
page
|
skip_missing
|
bool
|
Skip pages with no transcript at |
True
|
include_images
|
bool
|
Also export each page's image. |
True
|
prefix
|
str
|
Key prefix prepended to every item key. |
''
|
workers
|
int
|
Number of concurrent workers. |
8
|
doc_ids
|
Iterable[int] | None
|
Restrict to these document IDs ( |
None
|
validate_file
|
Callable[[Path], None] | None
|
Optional per-file validation callback. |
None
|
version_db
|
Path | None
|
Enable change detection — a transcript re-corrected in
Transkribus (new |
None
|
on_progress
|
Callable[[ExportStats], None] | None
|
Optional periodic progress callback. |
None
|
on_error
|
Callable[[str, Exception], None] | None
|
Optional per-item error callback. |
None
|
progress_interval
|
float
|
Minimum seconds between progress callbacks. |
5.0
|
Returns:
| Type | Description |
|---|---|
ExportStats
|
Export statistics. |
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/exporter.py
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | |
Models¶
models
¶
Pydantic models for the Transkribus REST API and export planning.
Transcript
¶
Bases: BaseModel
One transcript version attached to a page (newest matching first).
TsList
¶
Bases: BaseModel
Wrapper around a page's transcript versions.
Page
¶
Bases: BaseModel
A single page in a Transkribus document.
Document
¶
Bases: BaseModel
Document metadata as returned by the collection listing.
ItemKind
¶
Bases: StrEnum
What an export item holds — a page transcript or its page image.
ExportItem
¶
Bases: BaseModel
One planned export unit: a destination key and where to fetch its bytes.
key is a relative object key (no bucket prefix) usable both as a local
path under an output directory and as an S3 key. url is the source URL
to GET. When convert_alto is set, the fetched PAGE-XML is converted to
ALTO before being written/uploaded.
ts_id = None
class-attribute
instance-attribute
¶
Transkribus transcript version id — set for transcripts, drives change detection.
Change Detection¶
versions
¶
Persistent record of the last-synced Transkribus transcript version per key.
Transkribus versions transcripts server-side: re-correcting a page's ground truth
creates a new transcript version with a new tsId, but the exported object key
(derived from the page filename) is unchanged. The transfer tracker keys on that
path, so it would treat the corrected transcript as already-done and skip it.
This store remembers the tsId last synced for each object key. Comparing the
current tsId (from tsList.transcripts[0]) to the stored one tells a
scheduled re-run which transcripts changed and must be re-fetched — see
rahcp_transkribus.exporter.compute_stale_keys.
TranscriptVersionStore(db_path)
¶
SQLite-backed map of object key → last-synced transcript tsId.
A tiny sidecar DB (independent of the transfer tracker) so change detection adds no coupling to the generic tracker/bulk-transfer packages.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/versions.py
28 29 30 31 32 33 34 35 36 37 38 39 | |
path
property
¶
Path to the backing SQLite file.
get_all()
¶
Return the full {key: ts_id} map of previously-synced transcripts.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/versions.py
46 47 48 49 | |
set_many(versions)
¶
Upsert {key: ts_id} (last write wins per key).
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/versions.py
51 52 53 54 55 56 57 58 59 60 | |
close()
¶
Close the backing connection.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/versions.py
62 63 64 | |
ALTO Conversion¶
alto
¶
Optional PAGE-XML → ALTO-XML conversion via the page-to-alto CLI.
Requires the alto extra (pip install "rahcp-transkribus[alto]"), which
pulls in ocrd-page-to-alto and its page-to-alto console script. The
conversion shells out to that binary — a one-way PAGE→ALTO transform.
alto_available()
¶
Return whether the page-to-alto binary is on PATH.
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/alto.py
23 24 25 | |
page_bytes_to_alto(page_xml, *, alto_version='4.2')
¶
Convert PAGE-XML bytes to ALTO-XML bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
page_xml
|
bytes
|
PAGE-XML document bytes. |
required |
alto_version
|
str
|
Target ALTO schema version. |
'4.2'
|
Returns:
| Type | Description |
|---|---|
bytes
|
ALTO-XML document bytes. |
Raises:
| Type | Description |
|---|---|
TranskribusError
|
If |
Source code in packages/rahcp-transkribus/src/rahcp_transkribus/alto.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
rahcp-cli¶
S3 Commands¶
s3
¶
S3 CLI subcommands — thin wrappers over rahcp_client.
ls(ctx, bucket=typer.Argument(None), prefix=typer.Option('', '--prefix', '-p', help='Filter by key prefix'), max_keys=typer.Option(100, '--max-keys', '-n', help='Max results per page'), page=typer.Option(None, '--page', help='Continuation token for next page'), delimiter=typer.Option(None, '--delimiter', '-d', help='Group by delimiter (e.g. /)'), filter_key=typer.Option(None, '--filter', '-f', help='Filter keys containing this string'))
¶
List buckets (no args) or objects in a bucket.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
upload(ctx, bucket=typer.Argument(...), key=typer.Argument(...), file=typer.Argument(..., exists=True))
¶
Upload a local file (auto multipart if large).
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
download(ctx, bucket=typer.Argument(...), key=typer.Argument(...), output=typer.Option(None, '--output', '-o'))
¶
Download an object via presigned URL.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
rm(ctx, bucket=typer.Argument(...), keys=typer.Argument(...))
¶
Delete one or more objects.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | |
presign(ctx, bucket=typer.Argument(...), key=typer.Argument(...), expires=typer.Option(3600, '--expires'))
¶
Get a presigned download URL.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
upload_all(ctx, bucket=typer.Argument(...), source_dir=typer.Argument(..., help='Local directory to upload'), prefix=typer.Option('', '--prefix', '-p', help='Key prefix to prepend'), workers=typer.Option(0, '--workers', '-w', help='Concurrent workers (0 = use config)'), skip_existing=typer.Option(True, '--skip-existing/--overwrite', help='Skip files that already exist with matching size'), retry_errors=typer.Option(False, '--retry-errors', help='Only retry previously failed files'), include=typer.Option([], '--include', '-I', help="Only upload files matching these glob patterns (e.g. '*.jpg')"), exclude=typer.Option([], '--exclude', '-E', help="Skip files matching these glob patterns (e.g. '*.tmp')"), validate=typer.Option(False, '--validate', help='Validate each file before upload (auto-detects format by extension)'), verify=typer.Option(False, '--verify', help='Verify each upload by checking remote size after transfer'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN (overrides prefix and default)'), tracker_prefix=typer.Option(None, '--tracker-prefix', help="Prefix for tracker DB name (e.g. 'andraarkiv' → andraarkiv.upload-tracker.db)"), presign_batch_size=typer.Option(0, '--presign-batch-size', help='Presign URLs in batches of this size (0 = use config default)'))
¶
Upload a directory to S3 with tracked resume and parallel workers.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | |
download_all(ctx, bucket=typer.Argument(...), prefix=typer.Option('', '--prefix', '-p', help='Only download keys under this prefix'), dest_dir=typer.Option('.', '--output', '-o', help='Local destination directory'), workers=typer.Option(0, '--workers', '-w', help='Concurrent workers (0 = use config)'), retry_errors=typer.Option(False, '--retry-errors', help='Only retry previously failed files'), include=typer.Option([], '--include', '-I', help="Only download keys matching these glob patterns (e.g. '*.jpg')"), exclude=typer.Option([], '--exclude', '-E', help="Skip keys matching these glob patterns (e.g. '*.tmp')"), validate=typer.Option(False, '--validate', help='Validate each file after download (auto-detects format by extension)'), verify=typer.Option(False, '--verify', help='Verify each download by checking file size after transfer'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN (overrides prefix and default)'), tracker_prefix=typer.Option(None, '--tracker-prefix', help="Prefix for tracker DB name (e.g. 'backup' → backup.download-tracker.db)"), presign_batch_size=typer.Option(0, '--presign-batch-size', help='Presign URLs in batches of this size (0 = use config default)'))
¶
Download a bucket to a local directory with tracked resume and parallel workers.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 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 | |
verify(ctx, bucket=typer.Argument(...), source_dir=typer.Argument(..., help='Local directory to compare against'), prefix=typer.Option('', '--prefix', '-p', help='Key prefix (same as upload-all)'))
¶
Verify all local files exist in the bucket with matching sizes.
Source code in packages/rahcp-cli/src/rahcp_cli/s3.py
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | |
IIIF Commands¶
iiif
¶
IIIF CLI subcommands — download images from Riksarkivet IIIF endpoints.
download(ctx, batch_id=typer.Argument(..., help='Volume/batch ID (e.g. C0074667)'), output_dir=typer.Option('.', '--output', '-o', help='Output directory'), workers=typer.Option(0, '--workers', '-w', help='Concurrent downloads'), query_params=typer.Option(None, '--query-params', '-q', help="IIIF params (e.g. 'full/,1200/0/default.jpg')"), iiif_url=typer.Option(None, '--iiif-url', envvar='IIIF_URL', help='IIIF server base URL'), referer=typer.Option(None, '--referer', envvar='IIIF_REFERER', help='Referer header for servers that require one (e.g. https://sok.riksarkivet.se/)'), max_images=typer.Option(None, '--max-images', '-n', help='Limit number of images'), validate=typer.Option(False, '--validate', help='Validate each image after download'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help="Prefix for tracker DB name (e.g. 'familysearch' → familysearch.iiif-download.db)"))
¶
Download all images from a single IIIF batch.
Source code in packages/rahcp-cli/src/rahcp_cli/iiif.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | |
download_batches(ctx, job_file=typer.Argument(..., help='Text file with batch IDs (one per line)'), output_dir=typer.Option('.', '--output', '-o', help='Output directory'), workers=typer.Option(0, '--workers', '-w', help='Concurrent downloads'), query_params=typer.Option(None, '--query-params', '-q', help="IIIF params (e.g. 'full/,1200/0/default.jpg')"), iiif_url=typer.Option(None, '--iiif-url', envvar='IIIF_URL', help='IIIF server base URL'), referer=typer.Option(None, '--referer', envvar='IIIF_REFERER', help='Referer header for servers that require one (e.g. https://sok.riksarkivet.se/)'), max_images=typer.Option(None, '--max-images', '-n', help='Limit images per batch'), validate=typer.Option(False, '--validate', help='Validate each image after download'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help="Prefix for tracker DB name (e.g. 'familysearch' → familysearch.iiif-download.db)"))
¶
Download images from multiple IIIF batches listed in a text file.
Source code in packages/rahcp-cli/src/rahcp_cli/iiif.py
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | |
verify(ctx, job_file=typer.Argument(..., help='Text file with batch IDs (one per line)'), iiif_url=typer.Option(None, '--iiif-url', envvar='IIIF_URL', help='IIIF server base URL'), referer=typer.Option(None, '--referer', envvar='IIIF_REFERER', help='Referer header for servers that require one'), workers=typer.Option(16, '--workers', '-w', help='Concurrent manifest fetches'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help='Prefix for tracker DB name'), write_deficient=typer.Option(None, '--write-deficient', help='Write deficient batch IDs (one per line) to this file for re-running'))
¶
Reconcile downloaded images against live manifests; flag short/missing batches.
Re-fetches each batch's manifest and compares its image count to the tracker. Exits non-zero if any batch is short or missing, so it is usable as a gate.
Source code in packages/rahcp-cli/src/rahcp_cli/iiif.py
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | |
upload(ctx, bucket=typer.Argument(..., help='Target S3 bucket'), batch_ids=typer.Argument(None, help='Batch IDs to stream (e.g. C0074667). Omit when using --job-file.'), prefix=typer.Option('', '--prefix', '-p', help="Key prefix prepended to '<batch>/<image>'"), job_file=typer.Option(None, '--job-file', '-f', help='Text file with batch IDs (one per line)'), workers=typer.Option(0, '--workers', '-w', help='Concurrent download+upload workers'), skip_existing=typer.Option(True, '--skip-existing/--overwrite', help='Skip images already present in the bucket (HEAD check before download)'), validate=typer.Option(False, '--validate', help="Validate each image's bytes before upload"), verify=typer.Option(False, '--verify', help='Verify each upload by checking remote size after'), query_params=typer.Option(None, '--query-params', '-q', help="IIIF params (e.g. 'full/,1200/0/default.jpg')"), iiif_url=typer.Option(None, '--iiif-url', envvar='IIIF_URL', help='IIIF server base URL'), referer=typer.Option(None, '--referer', envvar='IIIF_REFERER', help='Referer header for servers that require one (e.g. https://sok.riksarkivet.se/)'), max_images=typer.Option(None, '--max-images', '-n', help='Limit images per batch'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help="Prefix for tracker DB name (e.g. 'familysearch' → familysearch.iiif-download.db)"))
¶
Stream IIIF images straight to S3 — download and upload in one pass (no local disk).
Bytes are fetched on the fly and pushed through the shared bulk transfer
engine, so this skips images already in the bucket (--skip-existing), can
validate bytes before upload (--validate) and verify size after (--verify) —
the same guarantees as s3 upload-all, without staging anything to disk.
Source code in packages/rahcp-cli/src/rahcp_cli/iiif.py
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 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 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 | |
Transkribus Commands¶
transkribus
¶
Transkribus CLI subcommands — export ground-truth collections to disk or a bucket.
export(ctx, collection_id=typer.Argument(..., help='Transkribus collection ID'), output_dir=typer.Option('.', '--output', '-o', help='Output directory'), status=typer.Option('GT', '--status', '-s', help='Transcript status filter (e.g. GT, FINAL)'), fmt=typer.Option('page', '--format', '-f', help='Output format: page or alto'), images=typer.Option(True, '--images/--no-images', help='Also export page images'), doc_ids=typer.Option(None, '--doc-ids', help='Restrict to these document IDs (comma-separated)'), workers=typer.Option(0, '--workers', '-w', help='Concurrent workers'), validate=typer.Option(False, '--validate', help='Validate each image after download'), transkribus_url=typer.Option(None, '--transkribus-url', envvar='TRANSKRIBUS_URL', help='TRP REST base URL'), username=typer.Option(None, '--username', '-U', envvar='TRANSKRIBUS_USERNAME', help='Transkribus login'), password=typer.Option(None, '--password', '-P', envvar='TRANSKRIBUS_PASSWORD', help='Transkribus password'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help='Prefix for tracker DB name'), check_updates=typer.Option(False, '--check-updates', help='Re-fetch transcripts corrected in Transkribus since last sync (tsId change)'), version_db=typer.Option(None, '--version-db', help='Change-detection state DB (default: <tracker-db>.versions.db)'), fail_on_error=typer.Option(True, '--fail-on-error/--no-fail-on-error', help='Exit non-zero if any item failed (cron-friendly; re-run resumes)'))
¶
Export a Transkribus collection (PAGE/ALTO XML + images) to a local directory.
Source code in packages/rahcp-cli/src/rahcp_cli/transkribus.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | |
upload(ctx, collection_id=typer.Argument(..., help='Transkribus collection ID'), bucket=typer.Argument(..., help='Target S3 bucket'), prefix=typer.Option('', '--prefix', '-p', help='Key prefix prepended to the export keys'), status=typer.Option('GT', '--status', '-s', help='Transcript status filter (e.g. GT, FINAL)'), fmt=typer.Option('page', '--format', '-f', help='Output format: page or alto'), images=typer.Option(True, '--images/--no-images', help='Also upload page images'), doc_ids=typer.Option(None, '--doc-ids', help='Restrict to these document IDs (comma-separated)'), on_conflict=typer.Option('skip', '--on-conflict', help='When a key already exists in the bucket: overwrite | skip | error'), archive_dir=typer.Option(None, '--archive-dir', help='Also write each file to this local directory (keeps a copy while streaming)'), workers=typer.Option(0, '--workers', '-w', help='Concurrent workers'), validate=typer.Option(False, '--validate', help="Validate each image's bytes before upload"), verify=typer.Option(False, '--verify', help='Verify each upload by checking remote size after'), fail_on_error=typer.Option(True, '--fail-on-error/--no-fail-on-error', help='Exit non-zero if any item failed (cron-friendly; re-run resumes)'), transkribus_url=typer.Option(None, '--transkribus-url', envvar='TRANSKRIBUS_URL', help='TRP REST base URL'), username=typer.Option(None, '--username', '-U', envvar='TRANSKRIBUS_USERNAME', help='Transkribus login'), password=typer.Option(None, '--password', '-P', envvar='TRANSKRIBUS_PASSWORD', help='Transkribus password'), tracker_db=typer.Option(None, '--tracker-db', envvar='RAHCP_TRACKER_DB', help='Tracker DB: file path or postgresql:// DSN'), tracker_prefix=typer.Option(None, '--tracker-prefix', help='Prefix for tracker DB name'), check_updates=typer.Option(False, '--check-updates', help='Re-upload transcripts corrected in Transkribus since last sync (implies overwrite)'), version_db=typer.Option(None, '--version-db', help='Change-detection state DB (default: <tracker-db>.versions.db)'))
¶
Stream a Transkribus collection straight to an HCP bucket, resumably.
Transcripts (and, unless --no-images, page images) are fetched from Transkribus and pushed through the shared bulk transfer engine in one pass. Built for unattended/cron use: the transfer tracker makes it idempotent (a re-run skips everything already done), --validate/--verify guard content, --on-conflict decides what happens for keys already in the bucket (overwrite | skip | error), and --fail-on-error (default) exits non-zero if anything failed so a cron can alert. Pass --archive-dir to also keep a local copy of every file as it streams, so one run does both the export and the bucket upload.
Source code in packages/rahcp-cli/src/rahcp_cli/transkribus.py
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 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 544 545 546 547 548 549 550 551 552 553 554 555 | |
Auth Commands¶
auth
¶
Auth commands — whoami.
whoami(ctx)
¶
Show current user info by decoding the JWT token.
Source code in packages/rahcp-cli/src/rahcp_cli/auth.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
Namespace Commands¶
namespace
¶
Namespace subcommands.
list_namespaces(ctx, tenant=typer.Argument(...), verbose=typer.Option(False, '--verbose', '-v'))
¶
List namespaces for a tenant.
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
get_namespace(ctx, tenant=typer.Argument(...), ns=typer.Argument(...), verbose=typer.Option(False, '--verbose', '-v'))
¶
Get namespace details.
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
create_namespace(ctx, tenant=typer.Argument(...), name=typer.Option(..., '--name'), quota=typer.Option(None, '--quota'))
¶
Create a namespace.
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
delete_namespace(ctx, tenant=typer.Argument(...), ns=typer.Argument(...))
¶
Delete a namespace.
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
import_namespace(ctx, tenant=typer.Argument(...), file=typer.Argument(..., exists=True, help='Exported template JSON file'))
¶
Create namespace(s) from an exported template file.
Examples:
rahcp ns export dev-ai my-ns > template.json rahcp ns import prod-tenant template.json
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
export_namespace(ctx, tenant=typer.Argument(...), ns=typer.Argument(...), output=typer.Option(None, '--output', '-o'))
¶
Export namespace as a reusable template.
Source code in packages/rahcp-cli/src/rahcp_cli/namespace.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
Configuration¶
config
¶
YAML config file with named profiles.
Config file: ~/.rahcp/config.yaml (or --config / RAHCP_CONFIG)
Example::
default: dev
profiles:
dev:
endpoint: http://localhost:8000/api/v1
username: admin
password: secret
tenant: dev-ai
verify_ssl: false
prod:
endpoint: http://localhost:8000/api/v1
username: prod-user
password: secret
tenant: prod-archive
Profile
¶
Bases: BaseModel
A named connection profile.
CLIConfig
¶
Bases: BaseModel
Config with named profiles.
resolve(name=None)
¶
Resolve a profile by name, falling back to default.
Source code in packages/rahcp-cli/src/rahcp_cli/config.py
91 92 93 94 95 96 97 98 | |
load_config(path=None)
¶
Load config from a YAML file.
Resolution: explicit path > RAHCP_CONFIG env > ~/.rahcp/config.yaml
Source code in packages/rahcp-cli/src/rahcp_cli/config.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | |
rahcp-etl¶
Pipeline¶
pipeline
¶
Pipeline DAG — stage registration, retry policy, checkpoint resume.
Stage(name, handler, retries=3, backoff=2.0)
dataclass
¶
A single pipeline stage with retry configuration.
Pipeline(checkpoint_store=None)
¶
Composable ETL pipeline with retry policy per stage.
Register stages with the @pipeline.stage() decorator, then run
with pipeline.run(payload). Each stage receives the output of
the previous stage. Checkpoints are saved after each successful stage
so the pipeline can resume on failure.
Example::
pipeline = Pipeline()
@pipeline.stage("extract", retries=3)
async def extract(payload):
...
return {"records": [...]}
@pipeline.stage("transform")
async def transform(payload):
...
return {"transformed": [...]}
result = await pipeline.run({"source": "s3://..."})
Source code in packages/rahcp-etl/src/rahcp_etl/pipeline.py
52 53 54 | |
stage(name, *, retries=3, backoff=2.0)
¶
Decorator to register a pipeline stage.
Source code in packages/rahcp-etl/src/rahcp_etl/pipeline.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
run(payload, *, pipeline_id=None)
async
¶
Execute all stages in order with retry and checkpointing.
Source code in packages/rahcp-etl/src/rahcp_etl/pipeline.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |
Consumer¶
consumer
¶
JetStream durable consumer for ETL work items.
ETLConsumer(nats_url, stream, subject, durable, *, max_deliver=5, ack_wait=30.0)
¶
Durable JetStream consumer with auto-reconnect.
Connects to NATS, binds a durable consumer on the given stream/subject, and dispatches messages to the handler. Acks on success, naks on failure.
Source code in packages/rahcp-etl/src/rahcp_etl/consumer.py
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
start(handler)
async
¶
Connect and start consuming messages.
handler receives the message payload as bytes and should
return a dict (success) or raise an exception (triggers nak/retry).
Source code in packages/rahcp-etl/src/rahcp_etl/consumer.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
stop()
async
¶
Stop consuming and disconnect.
Source code in packages/rahcp-etl/src/rahcp_etl/consumer.py
85 86 87 88 89 90 91 92 | |
Checkpointing¶
checkpointing
¶
KV-backed checkpoint storage for pipeline state.
CheckpointStore(kv)
¶
KV-backed progress checkpoints using NATS JetStream KV.
Each pipeline run gets a key: {pipeline_id} with value containing
the last completed stage and its output state. On failure, the pipeline
can resume from the last checkpoint instead of restarting.
Source code in packages/rahcp-etl/src/rahcp_etl/checkpointing.py
23 24 | |
create(nc, bucket='etl-checkpoints')
async
classmethod
¶
Create or bind to a KV bucket.
Source code in packages/rahcp-etl/src/rahcp_etl/checkpointing.py
26 27 28 29 30 31 32 33 34 35 | |
save(pipeline_id, stage, state)
async
¶
Save a checkpoint after a successful stage.
Source code in packages/rahcp-etl/src/rahcp_etl/checkpointing.py
37 38 39 40 41 42 43 44 45 46 | |
load(pipeline_id)
async
¶
Load the last checkpoint for a pipeline run.
Returns {"stage": str, "state": dict} or None if no
checkpoint exists.
Source code in packages/rahcp-etl/src/rahcp_etl/checkpointing.py
48 49 50 51 52 53 54 55 56 57 58 | |
clear(pipeline_id)
async
¶
Clear the checkpoint for a pipeline run.
Source code in packages/rahcp-etl/src/rahcp_etl/checkpointing.py
60 61 62 63 64 65 | |
Dead Letter Queue¶
dlq
¶
Dead-letter queue handler — route failed messages for inspection/replay.
DeadLetterHandler(js)
¶
Route permanently-failed messages to a DLQ stream for inspection/replay.
Messages in the DLQ include the original payload, the error message, and the original subject for targeted replay.
Source code in packages/rahcp-etl/src/rahcp_etl/dlq.py
29 30 | |
create(nc)
async
classmethod
¶
Create the DLQ handler, ensuring the DLQ stream exists.
Source code in packages/rahcp-etl/src/rahcp_etl/dlq.py
32 33 34 35 36 37 | |
send(subject, payload, error)
async
¶
Send a failed message to the DLQ.
Source code in packages/rahcp-etl/src/rahcp_etl/dlq.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | |
replay(*, filter_subject=None)
async
¶
Replay DLQ messages back to their original subjects.
Returns the number of messages replayed.
Source code in packages/rahcp-etl/src/rahcp_etl/dlq.py
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
purge(*, older_than=None)
async
¶
Purge DLQ messages, optionally only those older than older_than.
Returns the number of messages purged.
Source code in packages/rahcp-etl/src/rahcp_etl/dlq.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
rahcp-validate¶
Image Validation¶
images
¶
Image integrity checks — TIFF, JPEG and PNG corruption detection.
The checks are defined on bytes (magic markers + a full Pillow decode); the
Path validators simply read the file and delegate, so disk and in-memory
(streaming) callers share exactly one implementation.
ValidationError(source, reason)
¶
Bases: Exception
Raised when an image fails validation.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
20 21 22 23 | |
validate_tiff_bytes(data, *, source='<bytes>')
¶
Verify TIFF bytes: byte-order marker, version 42, and a full Pillow decode.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
38 39 40 41 42 43 44 45 46 47 | |
validate_jpg_bytes(data, *, source='<bytes>')
¶
Verify JPEG bytes: SOI/EOI markers and a full Pillow decode.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
50 51 52 53 54 55 56 | |
validate_png_bytes(data, *, source='<bytes>')
¶
Verify PNG bytes: signature and a full Pillow decode.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
59 60 61 62 63 | |
validate_tiff(path)
¶
Verify a TIFF file is not corrupt. Raises ValidationError on failure.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
69 70 71 72 | |
validate_jpg(path)
¶
Verify a JPEG file is not corrupt. Raises ValidationError on failure.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
75 76 77 78 | |
validate_png(path)
¶
Verify a PNG file is not corrupt. Raises ValidationError on failure.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
81 82 83 84 | |
validate_by_extension(path)
¶
Validate a file by its extension. Unknown extensions are skipped.
Supported: .jpg, .jpeg, .tif, .tiff, .png. Raises ValidationError if corrupt.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
106 107 108 109 110 111 112 113 | |
validate_bytes_by_extension(data, ext, *, source='<bytes>')
¶
Validate in-memory image bytes by extension (e.g. ".jpg").
Unknown extensions are skipped. Raises ValidationError if corrupt.
Source code in packages/rahcp-validate/src/rahcp_validate/images.py
116 117 118 119 120 121 122 123 124 125 | |
Rules¶
rules
¶
Pluggable validation rules — size, dimensions, extensions.
Rule(name, check)
dataclass
¶
A named validation check.
max_file_size(limit_bytes)
¶
Reject files larger than limit_bytes.
Source code in packages/rahcp-validate/src/rahcp_validate/rules.py
20 21 22 23 24 25 26 27 28 29 30 31 | |
image_dimensions(*, min_w=1, min_h=1, max_w=65535, max_h=65535)
¶
Check image dimensions are within bounds.
Source code in packages/rahcp-validate/src/rahcp_validate/rules.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
allowed_extensions(*exts)
¶
Only allow files with the given extensions (case-insensitive).
Source code in packages/rahcp-validate/src/rahcp_validate/rules.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | |
validate(path, rules)
¶
Run all rules against a file, collecting all failures.
Does not stop on the first failure — returns all validation errors.
Source code in packages/rahcp-validate/src/rahcp_validate/rules.py
81 82 83 84 85 86 87 88 89 90 91 92 | |