Known limitations¶
rustfits aims for parity with astropy and fitsio on the modern FITS feature set, but a handful of edge cases aren’t yet implemented (or are deliberately out of scope). This page lists them and shows the workaround where one exists.
Each item is tagged:
(not yet) — on the roadmap; will land when prioritized or when a user prompts for it. File an issue if you hit one.
(by design) — deliberately not supported; the workaround is the recommended path.
Tables¶
TNULL masking on VLA columns (not yet) —
mask_null=Trueworks for fixed B/I/J/K columns but raises on VLA columns with a TNULL card in the header. Read withmask_null=Falseand apply the mask yourself for now.Variable-length columns with TDIMn (not yet) — TDIMn on a P/Q column would reshape each heap cell to the declared dims, useful for VLA-of-images, but with a quirk: the spec only allows ONE variable axis per cell, so fully variable
(n, m)shapes aren’t expressible in FITS without padding.``TDISPn`` on write (by design) — the display-format hint isn’t emitted by rustfits’s writers. Add it by hand via
header["TDISP1"] = ...if you need it; it’s informational per the spec.
Images¶
The image surface (read, slice, write, extend, __setitem__,
BLANK / MaskedArray, BSCALE/BZERO, unsigned-int trick) is
feature-complete for both uncompressed and tile-compressed HDUs.
One narrow gap:
Per-tile ZBLANK column (not yet) — header-level ZBLANK on compressed integer images is supported. The convention also allows a per-tile column-form ZBLANK; rustfits doesn’t read it. Rare in practice — cfitsio typically emits the header form even for DITHER_2.
General¶
Multithreaded throughput (GIL release) (not yet) — rustfits releases the GIL only during remote (
http/ftp) downloads. The heavy CPU paths — tile decode/encode, large chunked I/O, checksum — currently hold it, so several Python threads calling into rustfits serialize on those. Single-threaded use is unaffected (the common case). Releasing the GIL around the pure-Rust decode/encode spans is a targeted future change, gated on a real multithreaded workload — file an issue if you have one.Random groups (
GROUPS=T,PTYPEn) (by design) — legacy format; vanishingly rare in new files. Not on the roadmap.
Files and compression¶
Whole-file gzip holds the file in RAM (by design) — a
.gzpath is decompressed into memory on open, and for a writable mode (r+/w+) the in-memory buffer is recompressed and written back (atomically) to the.gzpath onclose()orsync(). Because gzip isn’t randomly seekable and FITS needs random access, the entire file lives in RAM while open (the same caveat asmem://). The new bytes reach disk at close/sync (a finalizer flushes a forgotten-to-close file as a safety net, but the context manager is the reliable path).More to the point, prefer per-HDU compression over whole-file compression. Tile-compressed images (the
compress=...argument tocreate_image_hdu/write_image) and compressed tables (compress=...oncreate_table_hdu/write_table) generally beat a whole-file.gzon every axis that matters:Storage — the tile-compression conventions apply the codec suited to the data (RICE / HCOMPRESS / GZIP with byte-shuffle, optional float quantization), so they typically compress tighter than gzipping the raw byte stream.
Memory — a
.gzmust be decompressed in full into RAM before any byte is readable (gzip isn’t seekable; FITS needs random access). Tile compression decodes only the tiles you touch, keeping peak memory near the size of your slice rather than the whole image or table.Speed — partial and scattered reads pull only the needed tiles (and the per-tile cache makes repeated access cheap), whereas whole-file gzip pays the full-decompress cost up front on every open.
To write a compressed file, write a plain
.fitsand choose tile/HDU compression per HDU — see the compression sections of the image and table guides.
Cross-tool interop caveats¶
These aren’t rustfits limitations per se — they’re points where the FITS ecosystem disagrees and rustfits picks one side.
astropy ``1PX(N)`` parser — astropy 7.2.0 rejects bit- packed VLA columns (
1PX/1QX) withVerifyErrorduring column setup. rustfits writes spec-conforming1PX(maxbits)headers, but astropy can’t currently read them; fitsio (built on cfitsio) reads them fine. See the pinning testtest_astropy_pxqx_documented_limitation.astropy ``CompImageHDU`` verify_checksum — astropy’s
verify_checksumfor compressed HDUs has its own internal bug (TypeError on_compute_checksum(None)) that triggers on its own writes too. rustfits’s self-verify is correct; we don’t cross-verify ZHECKSUM against astropy.i8 (``TLONGLONG``) RICE compression — cfitsio’s encoder doesn’t support 64-bit RICE; fitsio refuses the write. rustfits rejects
compress=Rice1()+ i8 dtype upfront and points atcompress=Gzip2(...)instead. Files we wrote with i64 RICE would be unreadable everywhere except rustfits.