qiclib¶
qiclib is everything about where data sits on a QIC tape and what it means,
with no hardware attached: segments and their Reed-Solomon parity, the header
segment, the volume table, QIC-113 directories, QIC-122 compression, and the
cartridge and volume profiles. tapewyrm-cli hands it decoded sectors and gets
a TWTI image back; qicsilver and cinnabar use it to read that image.
Package facts¶
| Directory | packages/qiclib |
| Distribution | qiclib |
| Import name | qiclib |
| Scripts | none |
| Depends on | tapewyrm-archive only |
| Depended on by | tapewyrm-cli, qicsilver, cinnabar |
It is a library and is stdlib-only apart from tapewyrm-archive: no rich,
no click, no hardware code. The TOML profiles under qiclib/profiles/ and
qiclib/volume/profiles/ ship inside the wheel as package data.
Module map¶
Tape layout and recovery¶
| Module | Purpose |
|---|---|
qiclib.types |
Layout types: RawSector, Segment, SegmentStatus, SegmentResult, FileEntry, FileSet. |
qiclib.geometry |
Serpentine track geometry and the sector-ID coordinate algebra. |
qiclib.place |
Place recovered sectors into 32-sector segment bins by their own address. |
qiclib.merge |
Several passes merged: union (every CRC-good sector of every read) or select (each track or segment whole from its best read, Best and Rank). |
qiclib.vote |
Best out of N: a sector no read got right rebuilt by byte-wise majority (CRC-checked); a segment that will not correct rebuilt column by column (parity-checked). |
qiclib.mergelog |
The per-track and per-segment account of a merge that tw convert logs. |
qiclib.rs |
GF(256) Reed-Solomon erasure decoder (redundancy 3), per-segment correct, and verify, the spare-parity check that catches a CRC-good sector that is wrong. |
qiclib.segment |
Segment assembly helpers. |
qiclib.build |
Sectors to TWTI image: the layout half of tw convert (build_image). |
qiclib.qic100 |
QIC-100-MC, the GCR serial-recorded minicartridge format: record framing. Exercised only on synthetic data. |
What is on the tape¶
| Module | Purpose |
|---|---|
qiclib.header |
The header segment: format parameter record and bad-sector map, locate_header, apply_bsm, parse_header / parse_header_data. |
qiclib.cartridge |
Which cartridge a header's geometry implies, from the cartridge profiles and the standards' minimum-length equations (guess). |
qiclib.identify |
What is on a tape, from an image or from loose sectors (qicsilver identify, cinnabar): TapeInfo, from_image, from_sectors. |
qiclib.extract |
Image to TWVL volumes (qicsilver extract): read_volume_table, build_volume, extract, load_volume. |
The volume (qiclib.volume)¶
qiclib.volume re-exports nothing. Import the submodule you mean.
| Module | Purpose |
|---|---|
qiclib.volume.table |
The 128-byte VTBL records, VtblEntry, the fixed Rev N / QIC-113 reading of bytes 57-127, volume_streams. |
qiclib.volume.profile |
Volume profiles: the TOML loader, the [match] gate, scoring, guess, rank, contenders, and hand-tuned Overrides. |
qiclib.volume.verdict |
The shared vocabulary of a read: Check, the closed VolumeWarning enum, Probe, CLOSE_MARGIN. |
qiclib.volume.qic122 |
QIC-122 (Stac LZS) decompression and QIC-113 compression extents. |
qiclib.volume.qic113 |
QIC-113 Basic-DOS file sets (Rev G section 7). |
qiclib.volume.qic113ext |
QIC-113 extended-OS volumes (Rev G section 8): directory and data entries. |
qiclib.volume.fileset |
The file-set parser package. Importing it registers the built-in parsers. |
qiclib.volume.fileset.base |
FileSetParser, Reading, and the parser registry (register, get, names, probe_all). |
qiclib.volume.fileset.common |
What every parser shares: the TWVL header's VTBL and the standard checks. |
qiclib.volume.fileset.entry |
VolumeEntry, one entry as every parser reports it. |
qiclib.volume.fileset.qic113_basic |
Parser qic113-basic. |
qiclib.volume.fileset.qic113_extended |
Parser qic113-extended. |
qiclib.volume.fileset.qicstream_fset |
Parser qicstream-fset: QICStream v2 "FSET" volumes. |
qiclib.volume.fileset.none |
Parser none: a format that is recognised but cannot be listed. |
Tests and fixtures¶
| Module | Purpose |
|---|---|
qiclib.testing.builders |
Synthetic header segments, volume tables, QIC-113 volumes, extended volumes, FSET volumes and TWVL files. Shared with every package's tests. |
qiclib.testing.bench_3m, bench_qicextra, bench_unk_co |
Real header and volume-table bytes from three bench tapes. No file contents. |
How a tape is read¶
RawSector passes ──► place ──► merge (union | select) ──► vote ──► rs.correct ──► build_image ──► TWTI
│
TWTI ──► header ──► cartridge.guess │
└─► volume.table ──► volume.profile.guess ──► qic122 extents ──► fileset parser ──► TWVL
A volume is read in three layers, each named by the volume profile: the VTBL
field offsets ([vtbl]), the extent widths ([extent]) and the file-set
parser ([fileset]). See Writing a volume profile.
Key types and extension points¶
SegmentandRawSector(types) are what the physical layer hands in. A parser that may need the raw bytes later keeps them next to the parsed fields (VtblEntry.raw).- Parsers come in pairs:
parse_x(seg)wrapsparse_x_data(data), so tests and images can feed bytes directly. Follow the pair when you add one. - A new cartridge is a new TOML file in
qiclib/profiles/cartridge/. See Writing a volume profile. - A new backup program is a new TOML file in
qiclib/volume/profiles/, and a new parser only if no existing one reads its catalog. - A new file-set parser subclasses
FileSetParser, setsname,descriptionandparams, implementsread, and is registered by its module on import (add the import toqiclib/volume/fileset/__init__.py).probecomes free: it adds the standard checks and turns an exception into a fatal verdict, so a parser never crashes a probe. - A new warning is a new
VolumeWarningmember with a one-line meaning inWARNING_MEANINGS. Never a free string: warnings travel inidentify --json, the TWVL header,inspectand the tar's pax global header, and every consumer compares against members.
Test¶
cd packages/qiclib
uv sync --extra dev
uv run pytest
Or just check qiclib qiclib from the repository root. There is one
tests/test_<module>.py per module. Build tape structures with
qiclib.testing.builders; extend the builders rather than hand-rolling bytes
in a test. tests/test_volume_bench.py and tests/test_volume_profile.py
pin the bench-tape verdicts, so a profile change that moves a real tape's
reading fails a test.
Gotchas¶
qiclib/types.pyshadows the stdlibtypesmodule if you run Python withpackages/qiclib/qiclibas the working directory or onsys.path[0]. Run scripts from elsewhere, or withpython -I.- Nothing downstream re-decides.
qicsilver inspectandtarread the parser the TWVL header names. They probe every parser only for a header that names none. Do not add format sniffing downstream ofextract. - Real tapes lie about their flags. Never route a read on the vendor bit or the OS type; route on bytes the writer wrote. See Writing a volume profile.
- Holes stay holes. A byte range in an unrecovered segment is recorded as a hole in the TWVL, never shifted over.
qiclib.volume.tableis the reference. Theqic80-rev-nandqic113-rev-gprofiles must read every record as the fixed reading does;tests/test_volume_profile.pychecks it.