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 is read-only (not yet) — a
.gzpath is decompressed into memory on open, so reads work, butr+/w+on a.gzraise. Write-back (recompress-on-close) isn’t implemented.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.