FITS file object

class rustfits.FITS(filename, mode='r', *, lenient=False)

Bases: object

A FITS file open for reading, writing, or both.

Parameters:
  • filename (str) – Path to the FITS file.

  • mode ({'r', 'r+', 'w+'}, optional) –

    File open mode.

    • 'r' (default) — read-only; the file must exist.

    • 'r+' — read+write; the file must exist. Used to modify or append HDUs to an existing file.

    • 'w+' — read+write; creates the file if it doesn’t exist, truncates to zero length if it does. Equivalent to fitsio’s 'rw' + clobber=True.

  • lenient (bool, optional, keyword-only) –

    If False (default), header blocks must contain only printable ASCII bytes (0x20-0x7E); any other byte is rejected at open with a clear error. This is the right default for most workflows — if the file opened, every header byte was spec-compliant.

    If True, non-printable bytes are substituted in place with _ (matches astropy’s substitution rule) and parsing proceeds normally. Useful for archive files written by older non-conforming tools (IDL MWRFITS in particular emits keys with non-ASCII bytes the standard disallows). Lenient mode is READ-only — new card mutations through header[k] = v still go through strict validation.

Examples

FITS is the top-level entry point of rustfits. Open an existing file or create a new one, then index or iterate to reach individual HDUs:

# Read an existing file
with rustfits.FITS("data.fits") as fits:
    for hdu in fits:
        print(hdu.extname, hdu.has_data)
    sci = fits["SCI"]            # by EXTNAME
    hdu2 = fits[2]               # by position
    arr = fits[1].read()

# The default mode is "r", so the above is the same
# as as FITS(fname, "r")

# Open for read and write.
# Append a new HDU to an existing file
with rustfits.FITS("data.fits", "r+") as fits:
    fits.create_image_hdu("f4", (100, 100), extname="MODEL")
    fits[-1].write(model)

# Create a new file (or truncate an existing one)
with rustfits.FITS("out.fits", "w+") as fits:
    fits.create_table_hdu(my_dtype, nrows=1000)
    fits[1].write(rows)

Notes

Use as a context manager (with rustfits.FITS(...) as fits:) to guarantee the file handle is closed. len(fits) returns the HDU count; iteration yields each HDU in file order.

__getitem__(key, /)

Return self[key].

__iter__()

Implement iter(self).

__len__()

Return len(self).

close()

Close the file handle.

Called automatically when the FITS is used as a context manager (with rustfits.FITS(...) as fits:). Safe to call multiple times. After closing, attempting any read or write through the FITS object or its HDUs raises IOError.

Does NOT fsync: data is left in the OS page cache, which persists across normal program exit (matches fitsio and astropy). Power-loss or kernel-panic safety requires an explicit sync() call before close().

closed

True once close() (or context-manager __exit__) has been called; False while the file is open.

create_ascii_table_hdu(dtype, nrows=0, *, extname=None, extver=None, units=None, formats=None)

Create a new ASCII-table (XTENSION='TABLE') HDU and append it to the file.

Allocates the data section as ASCII spaces — call AsciiTableHDU.write() (or the returned HDU via fits[-1]) to write row data.

ASCII tables are rare in modern FITS files; most data pipelines use binary tables (create_table_hdu()). The ASCII form is provided for compatibility with tools that emit it (e.g. legacy pipelines, hand-edited files).

If the file has no HDUs yet, an empty primary image (SIMPLE=T, NAXIS=0) is written first — ASCII tables can’t be primary HDUs per the FITS standard.

Parameters:
  • dtype (numpy.dtype or list of tuples) –

    Structured dtype describing the table schema. Supported field dtypes (auto-mapped to TFORM):

    • i2 / i4 / i8I20

    • u1 / u2 / u4 / u8I20 + TZERO=2^63 (unsigned-int trick)

    • f4E15.7

    • f8D25.17

    • S<n> / U<n>A<n>

    b1 (bool) and i1 are rejected — neither has a native ASCII-table TFORM letter. Subarray fields and Object (VLA) dtypes are not supported (use BINTABLE).

  • nrows (int, optional) – Initial row count. Default 0.

  • extname (str, optional)

  • extver (int, optional)

  • units (dict, optional) – {column_name: unit_string} for TUNITn cards.

  • formats (dict, optional) – Per-column TFORM override, e.g. {"FLUX": "F12.4"}. Keys are case-insensitive; unmatched keys raise. Values are FITS TFORM strings (A<w> / I<w> / F<w>.<d> / E<w>.<d> / D<w>.<d>). Override letter must be compatible with the input dtype kind.

Raises:

ValueError – Negative nrows; unsupported field dtype; subarray or Object field; formats= override incompatible with the input dtype.

See also

create_table_hdu

Binary-table counterpart (preferred).

AsciiTableHDU.write

Write data into the created table.

create_image_hdu(dtype, dims, *, extname=None, extver=None, compress=None, quantize=None, blank=None)

Create a new image HDU and append it to the file.

Allocates the data section as zeros via sparse-file extension — call ImageHDU.write() (or use the returned HDU as fits[-1]) to actually write pixel data.

The first HDU created becomes the primary HDU (SIMPLE=T, EXTEND=T); subsequent calls produce XTENSION='IMAGE' extensions.

Parameters:
  • dtype (dtype-like) – Anything numpy.dtype() accepts: a short-code string ('f8', 'i4', 'u2'), a numpy scalar type (numpy.int32, numpy.float64), a Python builtin (int, float), or a numpy.dtype instance. Normalized internally via numpy.dtype(...). Both the BITPIX-native dtypes (u1 / i2 / i4 / i8 / f4 / f8) and the unsigned-int trick dtypes (i1 / u2 / u4 / u8) are accepted; the latter emit the corresponding BSCALE + BZERO cards.

  • dims (sequence of int) – Image shape in numpy (row-major) axis order — slowest axis first. Reversed internally to produce FITS NAXISn. Axis 0 (the slowest-varying axis) may be 0 to create an empty HDU that a later ImageHDU.extend() fills incrementally (parallel to create_table_hdu(nrows=0) + append). Every other axis must be > 0 — the FITS standard forbids zero-pixel inner axes. (HCOMPRESS_1 additionally requires every axis >= 4, so the empty-axis-0 form is unavailable under compress=Hcompress1(...).)

  • extname (str, optional) – EXTNAME to assign. Defaults to no EXTNAME card.

  • extver (int, optional) – EXTVER to assign. Defaults to no EXTVER card.

  • compress (Gzip1 / Gzip2 / Rice1 / Hcompress1 / Plio1, optional) – If set, create a tile-compressed image (BINTABLE + ZIMAGE on disk, returned in Python as a CompressedImageHDU) instead of a plain IMAGE extension. All five algorithms are supported for integer dtypes; only GZIP_1 / GZIP_2 for unquantized floats; all-but-PLIO for quantized floats.

  • quantize (Quantize, optional) – Per-tile quantization config for float-image compression: rustfits.Quantize(level=..., method='dither1', seed=0). Required when compress= is set and the dtype is f4/f8 and you want lossy storage. Ignored for integer dtypes. Omit on float input to write losslessly (raw float bytes through GZIP).

  • blank (int, optional) – Sentinel value for masked pixels (emits BLANK for uncompressed, ZBLANK for compressed integer HDUs). Only valid for integer dtypes; float HDUs use NaN.

Raises:

ValueError – Unsupported dtype, a non-positive inner dimension (axis 0 may be 0 but must not be negative), quantize= without compress=, blank= on a float dtype, or other algorithm/dtype incompatibilities (see Rice1 / Hcompress1 / Plio1 for per-algorithm constraints).

See also

create_table_hdu

The table-side counterpart.

Gzip1, Gzip2, Rice1, Hcompress1, Plio1

Quantize

Per-tile quantization config for float-image compression.

create_table_hdu(dtype, nrows=0, *, extname=None, extver=None, units=None, var_dtypes=None, bit_columns=None, heap_format=None, compress=None, ztilelen=None)

Create a new BINTABLE extension HDU and append it to the file.

Allocates the data section as zeros — call TableHDU.write() (or use the returned HDU as fits[-1]) to actually write row data.

If the file has no HDUs yet, an empty primary image (SIMPLE=T, NAXIS=0) is written first so the BINTABLE can land as an extension — the FITS standard forbids BINTABLE as the primary HDU.

Parameters:
  • dtype (numpy.dtype or list of tuples) – Structured dtype describing the table schema, or any form numpy.dtype() accepts (e.g. a “descr” list like [('x', 'f4'), ('y', 'f4'), ('name', 'S10')]). For VLA columns, use Object dtype ('O') for the field and declare its inner type via var_dtypes=.

  • nrows (int, optional) – Initial row count. Default 0; subsequent TableHDU.write() requires the value to match this exactly, while TableHDU.append() adds rows beyond it.

  • extname (str, optional) – EXTNAME to assign.

  • extver (int, optional) – EXTVER to assign.

  • units (dict, optional) – {column_name: unit_string} to populate TUNITn cards. Unspecified columns get no TUNIT.

  • var_dtypes (dict, optional) – For VLA columns: {column_name: inner_dtype_str}, where inner_dtype_str is a numpy short-code for the per-cell element type ('f4' / 'i4' / etc.) or 'S' for ASCII strings. The column itself must be declared as Object dtype ('O') in dtype.

  • bit_columns (list of str or True, optional) – Opt-in to bit-packed X storage for bool columns: a list of column names (case-insensitive) restricts the opt-in to those columns; True is a soft global toggle for ALL b1 columns. Default is one byte per bool (L).

  • heap_format ({'P', 'Q'}, optional) – Descriptor format for VLA columns. 'P' (default) uses 8-byte descriptors with a 4 GB heap ceiling; 'Q' uses 16-byte descriptors with no practical ceiling. Ignored when no VLA columns are declared.

  • compress (str, bool, or per-algorithm config / dict, optional) –

    Create a tile-compressed table (ZTABLE on disk, CompressedTableHDU in Python) instead of a plain BINTABLE. Accepts:

    • True — compress every column with cfitsio’s per-dtype defaults.

    • a string alias ('GZIP_1' / 'GZIP_2' / 'RICE_1') or config-class instance (Gzip1() / Gzip2() / Rice1()) — apply to every column.

    • a dict {column_name: algo} — per-column override; unspecified columns get the default.

  • ztilelen (int, optional) – Rows per tile for table compression. Defaults to cfitsio’s max(1, min(nrows, 10_000_000 / row_width)). Requires compress=; rejected otherwise.

Raises:

ValueError – Negative nrows; unsupported per-column dtype; var_dtypes= declared for a non-Object field; ztilelen= set without compress=; invalid compress= form; or an algorithm not legal for a column’s dtype (e.g. RICE_1 on float).

See also

create_image_hdu

The image-side counterpart.

TableHDU.write

Write data into the created table.

TableHDU.append

Add rows beyond nrows.

filename

Path passed to the constructor.

static from_bytes(data, mode='r', *, lenient=False)

Parse a FITS file from in-memory bytes (no disk access).

Parameters:
  • data (bytes) – Raw FITS bytes. Copied into a private in-memory buffer, so the returned FITS is fully independent of the original object.

  • mode ({'r', 'r+'}, optional) – 'r' (default) opens read-only; 'r+' allows in-memory mutation of the private copy. 'w+' is rejected because it would discard the bytes you just passed — use FITS("mem://", "w+") to create an empty in-memory file.

  • lenient (bool, optional, keyword-only) – Same semantics as the FITS constructor: if True, substitute non-printable header bytes with _ instead of rejecting. Default False (strict). See the FITS docstring for details.

hdus

List of HDU objects in file order.

Equivalent to iterating the FITS instance, but returns a real Python list (e.g. for slicing / length queries without consuming the iterator).

sync()

Force pending writes to disk (fsync(2)).

Optional durability for callers who must survive power loss or kernel panic between the last write() and program exit. Normal program crashes (SIGSEGV, SIGKILL, uncaught exception) do NOT lose data without this call – the kernel’s page cache persists across process death. Expensive: blocks until the storage device confirms the write. Cheap to call repeatedly when there are no new dirty pages. No-op on already-closed files.

For a .gz opened r+/w+ (whose bytes otherwise reach disk only at close()), sync recompresses the current in-memory buffer, writes it atomically to the .gz path, and fsyncs it – so the on-disk file is durable mid-session. It is a no-op when nothing has been mutated since the last sync.

to_bytes()

Return the FITS file’s current bytes as a Python bytes.

Primarily for in-memory files (mem:// / from_bytes()), where it extracts the file you built in RAM. Also works on a disk-backed file: it flushes, then reads the whole file into memory — note that the entire file lands in RAM, unlike the streaming read paths. Call it before close(), which drops the buffer.

write(data, *, extname=None, header=None)

Write data to a new HDU, auto-detecting image vs table.

Minimal-tier counterpart to write_image() and write_table() — accepts only the universal kwargs (extname, header) and dispatches on the data type:

Convenient for copying HDUs between files without caring about their type:

with rustfits.FITS(infile) as src:
    with rustfits.FITS(outfile, "w+") as dst:
        for hdu in src:
            if hdu.has_data:
                dst.write(hdu.read())

For knobs like compress=, quantize=, blank=, var_dtypes=, units=, etc., use the type-specific write_image() / write_table() directly.

Parameters:
  • data (numpy.ndarray or dict) – Image: a plain ndarray. Table: a structured ndarray or a {name: ndarray} dict.

  • extname (str, optional) – EXTNAME to set on the new HDU.

  • header (FITSHeader or dict, optional) – Cards to copy into the new HDU after the write.

Returns:

hdu – The newly created HDU, ready for further reads/writes while the FITS handle is open.

Return type:

ImageHDU, TableHDU, or compressed variant

See also

write_image

Image-specific create+write with all knobs.

write_table

Table-specific create+write with all knobs.

write_image(data, *, extname=None, extver=None, compress=None, quantize=None, blank=None, header=None)

Create an image HDU from data and write the pixels.

One-call convenience that combines create_image_hdu() and ImageHDU.write(). The HDU’s dtype and shape are taken from data; everything else is forwarded to create_image_hdu().

Parameters:
  • data (array_like) – The pixel data to write. Anything numpy.asanyarray accepts: an ndarray, a MaskedArray (mask is preserved through the write — see ImageHDU.write), or a nested Python sequence. Must have a supported numpy dtype (u1 / i1 / i2 / u2 / i4 / u4 / i8 / u8 / f4 / f8).

  • extname – Forwarded to create_image_hdu(); see that method for the full kwarg semantics.

  • extver – Forwarded to create_image_hdu(); see that method for the full kwarg semantics.

  • compress – Forwarded to create_image_hdu(); see that method for the full kwarg semantics.

  • quantize – Forwarded to create_image_hdu(); see that method for the full kwarg semantics.

  • blank – Forwarded to create_image_hdu(); see that method for the full kwarg semantics.

  • header (FITSHeader or dict, optional) – Cards to copy into the new HDU after the write. Routed through FITSHeader.update(), which silently skips protected/structural cards when copying from another FITSHeader and raises on protected cards in a dict.

Returns:

hdu – The newly created HDU, ready for further reads/writes while the FITS handle is open.

Return type:

ImageHDU or CompressedImageHDU

See also

create_image_hdu

Schema-only create (no data write).

write_table

The table-side counterpart.

write_table(data, *, names=None, extname=None, extver=None, units=None, var_dtypes=None, bit_columns=None, heap_format=None, compress=None, ztilelen=None, header=None)

Create a BINTABLE HDU from data and write the rows.

One-call convenience that combines create_table_hdu() and TableHDU.write(). The table schema (dtype + nrows) is derived from data; everything else is forwarded to create_table_hdu().

If the file has no HDUs yet, an empty primary image is written first (the FITS standard forbids BINTABLE as the primary HDU).

Parameters:
  • data (structured ndarray, dict, or list/tuple of arrays) –

    The rows to write. Three accepted shapes:

    • structured ndarray — dtype is taken from data.dtype, nrows from len(data).

    • dict {name: array} — dtype is composed field-by-field from each column’s ndarray dtype; all arrays must have the same length.

    • list / tuple of arrays + names= — same as dict, with names supplied as a separate argument.

  • names (sequence of str, optional) – Column names for the list/tuple input form. Required when data is a list or tuple; rejected for the other two forms (the names are already implied).

  • extname – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • extver – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • units – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • var_dtypes – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • bit_columns – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • heap_format – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • compress – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • ztilelen – Forwarded to create_table_hdu(); see that method for the full kwarg semantics.

  • header (FITSHeader or dict, optional) – Cards to copy into the new HDU after the write. Same semantics as write_image’s header=.

Returns:

hdu – The newly created HDU, ready for further reads/writes while the FITS handle is open.

Return type:

TableHDU or CompressedTableHDU

See also

create_table_hdu

Schema-only create (no data write).

write_image

The image-side counterpart.