Reading FITS files

fitsy.open(path, mode='readonly', lenient=True)

Open a FITS file by path.

Parameters:
  • path (str or os.PathLike) – Filesystem path to the FITS file.

  • mode ({'readonly', 'denywrite', 'update'}, optional) –

    'readonly' (default) opens read-only. Every mutation – a header edit, a pixel edit, FitsFile.append(), del file[i], and FitsFile.writeto() back onto the same path – raises ValueError. FitsFile.writeto() to a different path still works, and copies the file unchanged.

    'denywrite' behaves exactly like 'readonly'. fitsy does not take an OS-level write lock for this mode.

    'update' opens read/write. Header edits and image-pixel in-place edits (hdu.data[...] = x) are preserved on the next FitsFile.flush(), FitsFile.close(), or a clean __exit__. Table column data is read-only in this release; reconstruct the table with fitsy.bintable() to change column values.

    'append' and 'ostream' are recognized but not implemented; use fitsy.write() for output-only work.

  • lenient (bool, optional) –

    Tolerate common non-conforming headers so real-world files load. Default True. Pass lenient=False to require strict FITS conformance.

    Stray bytes in free-text comments are always sanitized to spaces, even when lenient=False.

    Leniency also accepts non-conforming values:

    • SIMPLE = F primary headers;

    • non-ASCII bytes in string values, sanitized to spaces;

    • lower-case or otherwise malformed keywords;

    • values matching no standard type, kept verbatim as a string so the rest of the file still loads;

    • stray bytes after END, a lower-case end, and broken CONTINUE chains.

    A present END, block alignment and the declared data size are enforced in every mode.

Returns:

FitsFile – A read-only or read/write handle depending on mode.

Raises:
  • ValueError – If mode is not one of the recognized values.

  • FitsError – On parse failures or I/O errors.

Examples

>>> import fitsy
>>> with fitsy.open("image.fits") as f:
...     img = f[0]
...     print(img.axes)
class fitsy.FitsFile

Bases: object

Owning, ordered, mutable list of HDUs.

Each slot is a typed object – ImageHdu, BinTable, AsciiTable, or RandomGroups – owning its own header and data, and it outlives the file handle.

In mode='readonly' (the default), every mutation raises ValueError: a header edit, hdu.data[...] = x, f.append(hdu), del f[i], and writeto() back onto the source path. writeto() to a different path still works and copies the file unchanged.

In mode='update', the same edits are held in memory and reach the source file on the next flush(), close(), or a clean __exit__. A pixel patch through f[i].section[a:b] = arr is written immediately instead, and does not wait for flush().

Build a new, in-memory file with the FitsFile() constructor (no path), then append() each HDU and call writeto() to create a file from nothing.

Use the open() factory to load an existing file rather than constructing this class directly.

Notes

A RandomGroups HDU writes back the header and the data section it was read with. The bindings expose no way to edit either, so a write reproduces the source HDU byte for byte.

Examples

>>> with fitsy.open("image.fits", mode="update") as f:
...     f[0].data[0, 0] = 42.0
...     # changes flushed automatically on __exit__
>>> with fitsy.open("image.fits") as f:    # readonly
...     f.writeto("copy.fits")             # unmodified copy
__enter__()

Context-manager entry.

Returns:

FitsFileself.

__exit__(exc_type=None, _exc_val=None, _exc_tb=None)

Context-manager exit.

On a clean exit – no exception in flight – calls flush() and lets any error it raises propagate.

On exit due to an in-flight exception, makes any already applied in-place pixel patch durable, but does not rewrite the file for a pending header or full-array edit; any error from that best-effort step is discarded.

Returns:

bool – Always False. An in-flight exception is never suppressed.

__getitem__(key, /)

Return self[key].

__len__()

Return len(self).

add_checksums()

Enable CHECKSUM / DATASUM stamping on every HDU that writeto() emits, and on every HDU that flush() rewrites.

When enabled, every HDU written gains freshly computed CHECKSUM and DATASUM cards (per the FITS Checksum Proposal). An existing placeholder card in the header is overwritten in place; a missing one is inserted. The flag stays on for the lifetime of the FitsFile object; there is no way to turn it back off or to stamp only one HDU.

Notes

This does not stamp anything immediately. fitsy computes each value during a write, when the final byte layout of the HDU is known. Both writeto() and flush() then stamp every HDU. This call marks the file as needing a rewrite, so a flush() with no other pending edit still writes the cards. To check the result, call verify_checksums() on the written file.

append(value)

Append an HDU at the end.

Parameters:

value (ImageHdu or BinTable or AsciiTable) – The new HDU. This also accepts a builder, meaning an fitsy.ImageBuilder, a fitsy.BinTableBuilder or an fitsy.AsciiTableBuilder, as returned by fitsy.image(), fitsy.bintable() or fitsy.ascii_table(). A builder is promoted to a live, independently editable HDU instance.

Raises:
  • ValueError – If the file was opened read-only.

  • TypeError – If value is not an HDU instance or a builder.

Notes

Marks the file dirty, and invalidates every cached in-place pixel-patch binding, before value is checked. A call that raises TypeError still forces the next write to be a full rewrite.

close()

Flush pending edits (if any) and release the source file handle.

After close(), the slot list and any HDU wrapper Python already holds remain usable as in-memory data, but the underlying file handle is dropped: reading a still-Pending slot then raises ValueError. writeto() always visits every slot, so it raises the same way if any slot is still Pending. A later flush() call raises only if it decides a rewrite is needed and a Pending slot remains; with nothing left to write, it is a successful no-op.

Idempotent: calling close() more than once is safe.

Raises:

TypeError, ValueError, FitsError – Under the same conditions as flush(), which this method calls first.

flush()

Flush pending edits to disk.

A no-op when the file was opened mode='readonly' or mode='denywrite'.

In mode='update', a mutation that an in-place pixel patch cannot satisfy – a header edit, hdu.data = new_array, append(), del file[i], a fancy or dtype-mismatched section[...] write, or an edit on a tile-compressed image – rewrites the whole file through a sibling temp file and an atomic rename. A slot the caller never touched is streamed byte-for-byte from the original file, with no decode or re-encode. Reading hdu.data alone does not by itself force a rewrite: fitsy re-reads that HDU’s data section from disk and compares it against the cached array, and rewrites only if the two differ.

Mixing modes: if you issue an in-place section[...] patch and then a non-patch mutation in the same session, the patch reaches disk first, through pwrite, and the subsequent flush() then performs a full rewrite that includes the patched bytes, by streaming the already-patched source file. The patch is not lost.

Crash safety: an in-place patch uses pwrite with no undo journal; a process death mid-patch can leave the file with some rows updated and others not. The full-rewrite path is crash-safe, because it writes to a sibling temp file and renames atomically once the bytes are durable. The parent directory is not separately fsynced, so a power loss between the rename and the next directory commit can, in theory, leave the rename invisible after reboot on a non-journaling filesystem. A stale .fitsy-tmp.* sibling left by a crashed rewrite is harmless and may be deleted.

Raises:
  • TypeError – If a rewrite is needed and an HDU slot holds an object that is none of the four wrapper classes; see writeto().

  • ValueError – If a rewrite is needed and would leave zero HDUs, or if an internal lock was poisoned by an earlier panic.

  • FitsError – On an I/O failure, or if an HDU cannot be encoded for write.

hdu(i)

Return the i-th HDU. Equivalent to file[i] for non-negative integer i.

Parameters:

i (int) – HDU index. Unlike file[i], does not accept a negative index.

Returns:

ImageHdu or BinTable or AsciiTable or RandomGroups – The matching HDU.

Raises:
hdu_by_name(name, ver=None)

Return the first HDU with matching EXTNAME.

Parameters:
  • name (str) – Value of the EXTNAME keyword to match.

  • ver (int, optional) – Value of the EXTVER keyword to also require. Default None, which matches on name alone, regardless of EXTVER. When ver is given, an HDU with no EXTVER card is treated as EXTVER=1.

Returns:

ImageHdu or BinTable or AsciiTable or RandomGroups – The matching HDU.

Raises:

KeyError – If no HDU matches name (and ver, when given).

Notes

Materializes each HDU, in order, until a match is found or the list is exhausted.

insert(i, value)

Insert an HDU at position i.

Parameters:
Raises:
  • ValueError – If the file was opened read-only.

  • TypeError – If value is not an HDU instance or a builder.

Notes

Marks the file dirty, and invalidates every cached in-place pixel-patch binding, before value is checked. A call that raises TypeError still forces the next write to be a full rewrite.

read_only

True when the file was opened read-only.

verify_checksums()

Verify per-HDU CHECKSUM and DATASUM cards.

Streams the data section of each HDU directly from disk in fixed-size chunks (no full materialization) and compares against the values stored in the HDU header. HDUs that have neither card are reported with both fields None; HDUs that only have one of the two are reported with the missing field None and the present one as True / False.

Returns:

list[dict] – One dict per HDU, in file order. Keys:

  • hdu – 0-based HDU index (int).

  • checksum_okTrue/False/None.

  • datasum_okTrue/False/None.

Raises:
  • ValueError – If the file has no backing path – built with the FitsFile() constructor rather than fitsy.open() – or if close() was already called.

  • FitsError – On an I/O failure, or if a header fails to parse.

Notes

Reads the header and data bytes currently on disk, in both mode='readonly' and mode='update'. An in-memory edit that has not yet reached the file through flush(), close(), or a clean __exit__ is not reflected.

wcs(i=0, alt=' ')

Resolve the WCS for the given HDU index.

Parameters:
  • i (int, optional) – HDU index. Default 0 (primary HDU). Does not accept a negative index.

  • alt (str, optional) – Single ASCII character. ' ' (default) selects the primary WCS description; 'A' through 'Z' select alternate descriptions.

Returns:

Wcs or NoneNone if HDU i’s header carries no WCS for alt.

Raises:
  • IndexError – If i is at least len(file).

  • OverflowError – If i is negative.

  • ValueError – If alt is not exactly one character.

  • FitsError – If alt is not ' ' or one of 'A'-'Z', if the header carries a malformed WCS, or if a -TAB axis cannot be resolved (see Notes).

Notes

A -TAB axis (Paper III Sec.6) stores its coordinate array in a sibling BINTABLE. The PSi_0 / PVi_1 cards name that table. This method loads the table from the file this handle was opened from. A handle built in memory (the FitsFile() constructor) has no file to search. A -TAB axis then raises here, not later at transform time. Use fitsy.Wcs(f[i].header) to inspect such a header without the lookup table.

writeto(path, overwrite=False)

Write the file (with all in-memory edits) to path.

Each HDU is re-emitted from its current Python state:

  • ImageHdu – pixel data is encoded from the live numpy array (so hdu.data[...] = x round-trips); BITPIX and NAXIS* are recomputed from the array.

  • BinTable, AsciiTable – data bytes are re-emitted as captured at load time (column edits do not round-trip in this release).

  • RandomGroups – header and data section are re-emitted as captured at load time.

An HDU slot that is still Pending (never accessed) streams through unchanged, whatever its kind.

If the first HDU is not an image, an empty primary image HDU (NAXIS = 0) is automatically prepended so the output is a valid FITS file.

The on-disk source file (if any) is never modified, except when path resolves to the same file the handle was opened from – a self-write requires update mode and triggers an in-place rewrite (alias for flush()).

Parameters:
  • path (str or os.PathLike) – Destination path.

  • overwrite (bool, optional) – If False (default), raise FileExistsError when path already exists. Set to True to replace it.

Raises:
class fitsy.ImageHdu(data, header=None, name=None)

Bases: object

Image HDU with lazy numpy data.

Returned by FitsFile.hdu() (or file[i]) for an image HDU. Pixels are read on the first access to hdu.data, not before, except for a tile-compressed image, which is decoded in full as soon as the HDU is materialized. Later accesses return the same array, and in-place edits like hdu.data[0, 0] = 42 are kept on the next FitsFile.writeto().

For images larger than RAM, use section: hdu.section[a:b] reads only those bytes and hdu.section[a:b] = arr writes only those, never materializing the whole array. A tile-compressed image gains nothing from section, since its array is already resident by the time hdu.section can be used.

Examples

>>> with fitsy.open("image.fits") as f:
...     img = f[0]
...     print(img.bitpix, img.axes, img.data.shape)
axes

[NAXIS1, NAXIS2, ...].

When the pixel data has been materialized, the axes are reported from the live numpy array shape (reversed, since numpy is row-major while FITS lists fastest-varying first). Otherwise the axes recorded at HDU-open time are returned – this is the lazy path that does not trigger a data read.

Type:

Image axes in NAXIS order

bitpix

FITS BITPIX value (e.g. -32 for f32).

data

Pixel data as a numpy array.

Materializes the array on first access by reading the data section from disk, byteswapping into native order, and applying BSCALE/BZERO/BLANK scaling. Subsequent accesses return the same array, and in-place mutation (hdu.data[...] = x) is preserved by the next FitsFile.writeto(), and, in mode='update', by FitsFile.flush().

For images that do not fit in RAM, prefer sectionhdu.section[a:b] reads only the requested bytes without materializing the full array.

Returns:

numpy.ndarray or NoneNone when the HDU has no data section (NAXIS == 0). Otherwise, the dtype depends on BZERO, BSCALE and BLANK, not on BITPIX alone:

  • If BZERO is 0, BSCALE is 1 and BLANK is absent, the raw pixels are returned. The dtype follows BITPIX.

  • If BSCALE is 1 and BZERO is the standard integer offset, the matching unsigned dtype is returned. BITPIX 8 with BZERO -128 returns int8.

  • For all other scaling, BSCALE, BZERO and BLANK are applied, and the result is floats. A pixel whose stored value matches BLANK – or that was already nan in a floating-point image – becomes nan. BITPIX 8, 16 and -32 give float32. All other values give float64.

An array obtained from a read-only FitsFile has its WRITEABLE flag cleared; assigning into it raises ValueError.

Raises:

FitsError – If the pixel bytes cannot be read from the source file.

Notes

A tile-compressed image is decoded in full when the HDU is materialized, not on this first .data access; by the time Python code can reach hdu.data, the array already exists.

Reading .data in mode='update' does not, by itself, force the file to be rewritten. The next FitsFile.flush() compares this array’s bytes against the file and rewrites only if they differ.

data_matches_source()

Whether the cached pixel array still matches the bytes on disk.

Returns false when that cannot be established (no read source), so the caller falls back to rewriting.

A scaled HDU is compared in physical units, because that is what the cache holds. Comparing it against the stored integers would never match, and merely reading data would then rewrite the file in a different BITPIX.

header

The HDU header (see Header).

section

Slicing accessor that mirrors numpy.ndarray indexing. hdu.section[a:b, c:d] reads only the requested region from disk – no full-image materialization.

In mode='update', hdu.section[a:b] = arr writes only the touched bytes back via positional pwrite, again without materializing the full image. This is the supported way to read or patch sub-regions of an image bigger than available RAM.

In-place writes require contiguous slicing (start:stop with step 1). Fancy indexing and negative steps raise a ValueError – assign through hdu.data[...] to trigger a full-file rewrite instead.

A scaled HDU (BSCALE, BZERO or BLANK) is written in the same physical units data reports. fitsy inverts the scaling and leaves the stored BITPIX alone.

If hdu.data has already been accessed (and is therefore resident in memory), reads and writes go through the in-memory array for consistency with subsequent hdu.data accesses.

Returns:

_ImageSection – Slicing proxy. Use section[i, j, k] exactly like data[i, j, k].

wcs(alt=' ')

Resolve the WCS for this HDU.

Parameters:

alt (str, optional) – Single ASCII character. ' ' (default) selects the primary description; 'A' through 'Z' select alternate descriptions.

Returns:

Wcs or NoneNone if the header carries no WCS for alt.

Raises:

FitsError – If alt is not ' ' or one of 'A'-'Z', if the header carries a malformed WCS, or if a -TAB axis cannot be resolved (see Notes).

Notes

A -TAB axis (Paper III Sec.6) stores its coordinate array in a sibling BINTABLE. The PSi_0 / PVi_1 cards name that table. An HDU from fitsy.open() keeps a handle to its file. This method resolves the lookup through that handle, exactly as fitsy.FitsFile.wcs() does. An HDU built in memory has no file to search. A -TAB axis then raises here, not later at transform time. Use fitsy.Wcs(hdu.header) to inspect such a header without the lookup table.

class fitsy.BinTable

Bases: object

Binary table HDU (BINTABLE).

Returned by FitsFile.hdu() (or file[i]) when the HDU kind is BINTABLE. Columns are decoded eagerly. A column with a repeat count of 1 and a plain integer or real TFORM code returns a 1-D numpy.ndarray; every other column – logical, string, a repeat count above 1, or a bit, complex or variable-length code – returns a Python list, one entry per row. See column() for the full mapping from TFORM code to Python type.

Examples

>>> with fitsy.open("catalog.fits") as f:
...     tbl = f[1]
...     ra = tbl["RA"]   # numpy array
...     name = tbl["NAME"]  # list[str]
__getitem__(key, /)

Return self[key].

column(name)

Column accessor; equivalent to table[name].

Parameters:

name (str) – Column name (TTYPEn), case-sensitive.

Returns:

numpy.ndarray, numpy.ma.MaskedArray, or list – The decoded column, keyed by TFORM code (Standard Table 18):

  • B, I, J, K, repeat 1, with no TSCALn/TZEROn, or B under the FITS signed-byte convention (TSCALn = 1, TZEROn = -128): numpy.ndarray of int64, or numpy.ma.MaskedArray of int64 if a cell’s stored value matches TNULLn.

  • E or D, repeat 1: numpy.ndarray of float64. E is widened from its 32-bit storage. TNULLn has no effect on these two codes.

  • B, I, J, K, repeat 1, with any other TSCALn/TZEROn: numpy.ndarray of float64, scaled as TZEROn + TSCALn * stored. A cell whose stored value matches TNULLn becomes nan.

  • A, repeat 1, no TDIMn: list of str, right-trimmed of trailing spaces and NUL bytes.

  • L, repeat 1: list of bool or None (None marks an undefined logical, stored as a NUL byte).

  • Every other case – a repeat count above 1 (a fixed-size vector cell, or A with TDIMn), X, C, M, P/Q, or I/J/K under the FITS unsigned-integer convention (TSCALn = 1, TZEROn = 2**(8n-1)): a list, one entry per row:

    • A numeric vector cell is a numpy array, reshaped to TDIMn in C order (fastest-varying last, the reverse of the TDIMn order) when present.

    • An X cell is a bool numpy array, unpacked most-significant bit first.

    • A C/M cell is a list of (re, im) float tuples, one per repeat element. fitsy does not build a Python complex value.

    • A P/Q cell is a numpy array decoded from the heap, in the descriptor’s inner type, with that type’s own null and scaling rule.

    • An A cell with TDIMn is a numpy array of str.

Raises:

KeyError – If name names no column.

Notes

fitsy decides a column’s representation from its first row. If a later row disagrees, the whole column falls back to the per-row list form. An empty table (n_rows is 0) always returns an empty list for every column.

The returned array, when one is returned, is read-only. fitsy freezes only that outer array. A numpy array held inside a returned list stays writable, but fitsy rebuilds the column on every call, so such an edit reaches neither the file nor the next call.

column_names

List of column names in declaration order.

data

Pre-decoded columns assembled into one numpy structured array, one record per row.

Returns:

numpy.recarray – Every column, in declaration order. A column column() returns as a numpy array keeps that array’s dtype here. A column column() returns as a list is encoded as object dtype instead, one row’s list entry per cell. The array is read-only.

Raises:

Exception – The numpy exception, unchanged, if a numpy call fails while fitsy assembles the array.

Notes

This array is rebuilt on every access. An edit never reaches the file, or even the next call to data; the array is frozen read-only so an edit raises instead of silently vanishing.

A masked int64 column, or an L column holding None, loses that null marker here: the masked or undefined cell reads back as 0 or False.

header

The HDU header.

n_rows

Number of rows in the table.

row(r)

Build a row dict for row index r.

Parameters:

r (int) – Row index. Must satisfy 0 <= r < n_rows. Unlike table[r], this method does not accept a negative index.

Returns:

dict – One entry per column, keyed by column name, holding that row’s value in the same form column() would give for the whole column.

Raises:

IndexError – If r is outside -n_rows to n_rows - 1.

Notes

A negative r counts back from the last row, as table[r] does. table[r] and this method accept the same indices.

to_dict()

Materialize every column as a plain dict[str, ndarray | list].

Returns:

dict – One entry per column, keyed by column name, in declaration order. Each value is what column() returns for that name.

class fitsy.AsciiTable

Bases: object

ASCII TABLE HDU.

Returned by FitsFile.hdu() (or file[i]) when the HDU kind is TABLE. A numeric column (TFORM code I, F, E or D) decodes to a numpy.ndarray of float64, with nan for a cell matching TNULLn. A character column (A) decodes to a list of str. See column() for the full decoding rule.

__getitem__(key, /)

Return self[key].

column(name)

Column accessor; equivalent to table[name].

Parameters:

name (str) – Column name (TTYPEn), case-sensitive.

Returns:

numpy.ndarray or list of strI, F, E and D (Standard Table 15) all return a 1-D numpy.ndarray of float64, scaled by TSCALn/TZEROn when either is set. A blank field parses as 0 before scaling, so it becomes TZEROn once scaled. A field matching TNULLn becomes nan. A returns a list of str, one per row, padded to the field width; fitsy does not trim it.

Raises:

KeyError – If name names no column.

Notes

The returned array, when one is returned, is read-only.

column_names

List of column names in declaration order.

data

Every column assembled into one numpy structured array, one record per row.

Returns:

numpy.recarray – Every column, in declaration order, with the same dtype column() gives it. The array is read-only.

Notes

This array is rebuilt on every access. An edit never reaches the file, or even the next call to data; the array is frozen read-only so an edit raises instead of silently vanishing.

header

The HDU header.

n_rows

Number of rows in the table.

row(r)

Build a row dict for row index r.

Parameters:

r (int) – Row index. Must satisfy 0 <= r < n_rows. Unlike table[r], this method does not accept a negative index.

Returns:

dict – One entry per column, keyed by column name, holding that row’s value in the same form column() would give for the whole column.

Raises:

IndexError – If r is outside -n_rows to n_rows - 1.

Notes

A negative r counts back from the last row, as table[r] does. table[r] and this method accept the same indices.

to_dict()

Materialize every column as a plain dict[str, ndarray | list].

Returns:

dict – One entry per column, keyed by column name, in declaration order. Each value is what column() returns for that name.

class fitsy.RandomGroups

Bases: object

Random-groups primary HDU (legacy format; see Standard Sec.6).

Read-only Python view: groups are decoded on demand through group(). Indexing the file to reach this HDU (for example, file[0]) materializes it in memory.

This class exposes no way to edit the header or the groups. FitsFile.writeto() and FitsFile.flush() write back the header and the data section the HDU was read with, so the written HDU matches the source byte for byte.

bitpix

BITPIX value.

data_per_group

Number of data values per group (prod(NAXIS2..NAXISn)).

group(i)

Decode one group as (parameters, data) numpy arrays.

Parameters:

i (int) – Group index, 0-based. Does not accept a negative index.

Returns:

tuple of numpy.ndarray(parameters, data). Both arrays share the HDU’s BITPIX dtype and are read-only. parameters has length n_params; data has length data_per_group. Neither BSCALE/BZERO nor PSCALn/PZEROn is applied – both arrays hold the stored, unscaled values.

Raises:
header

HDU header.

n_groups

Number of groups (GCOUNT).

n_params

Number of parameters per group (PCOUNT).

Convenience functions

fitsy.getdata(path, ext=None, *, header=False)

Read one HDU’s data, and optionally its header, from path.

Parameters:
  • path (str or os.PathLike) – File to read.

  • ext (int or str, optional) – HDU index or EXTNAME. Default is HDU 0. When ext is omitted and HDU 0 carries no data, fitsy reads HDU 1 instead.

  • header (bool, keyword-only, optional) – Default False. When True, return (data, header) instead of only data.

Returns:

numpy.ndarray or tuple – Pixel data for an image HDU. A read-only structured array, one record per row, for a binary or ASCII table HDU. When header is True, returns (data, header) instead, paired with the Header of the HDU the data came from.

Raises:
  • FitsError – If path cannot be opened or parsed as FITS.

  • IndexError – If ext is an out-of-range integer. Also raised if the selected HDU has no data, and, when ext was omitted, HDU 1 has no data either.

  • KeyError – If ext is a string that names no HDU.

  • TypeError – If ext is neither an int, a str, nor omitted.

Notes

A random-groups primary HDU (GROUPS = T) has neither a data array nor a dict form. Reading one with ext omitted raises AttributeError instead of the exceptions above.

fitsy.getheader(path, ext=None)

Read one HDU’s header from path.

Parameters:
  • path (str or os.PathLike) – File to read.

  • ext (int or str, optional) – HDU index or EXTNAME. Default is HDU 0.

Returns:

Header – Header of the selected HDU.

Raises:
  • FitsError – If path cannot be opened or parsed as FITS.

  • IndexError – If ext is an out-of-range integer.

  • KeyError – If ext is a string that names no HDU.

  • TypeError – If ext is neither an int, a str, nor omitted.

fitsy.getval(path, key, ext=None)

Read one header keyword from path.

Parameters:
  • path (str or os.PathLike) – File to read.

  • key (str) – Header keyword to read.

  • ext (int or str, optional) – HDU index or EXTNAME. Default is HDU 0.

Returns:

bool, int, float, complex, str, or None – Value of the card. A HeaderCommentary object for a COMMENT, HISTORY, or blank keyword.

Raises:
  • FitsError – If path cannot be opened or parsed as FITS.

  • IndexError – If ext is an out-of-range integer.

  • KeyError – If ext is a string that names no HDU, or if key is absent from the selected header.

  • TypeError – If ext is neither an int, a str, nor omitted.

fitsy.info(path)

Return a brief HDU summary table for path.

Parameters:

path (str or os.PathLike) – File to read.

Returns:

list of tuple – One (index, name, ver, kind, dims_or_n_rows) tuple per HDU, in file order.

  • index – 0-based HDU position (int).

  • nameEXTNAME, or an empty string if absent (str).

  • verEXTVER, or 1 if absent (int).

  • kind – wrapper class name: "ImageHdu", "BinTable", "AsciiTable", or "RandomGroups". "Unknown" in the unexpected case where fitsy cannot read the HDU’s Python type name.

  • dims_or_n_rows – axis lengths (list of int) for an image HDU, row count (int) for a table HDU, or None for a random-groups HDU.

Raises:

FitsError – If path cannot be opened or parsed as FITS.

Comparing files

fitsy.diff(a, b, *, rtol=0.0, atol=0.0, max_diffs=10, ignore_keywords=None)

Compare two FITS files and return the differences.

Parameters:
  • a (str or os.PathLike) – Paths to the two files to compare.

  • b (str or os.PathLike) – Paths to the two files to compare.

  • rtol (float, optional) – Relative tolerance for floating-point comparisons. Default 0.0 (exact equality).

  • atol (float, optional) – Absolute tolerance for floating-point comparisons. Default 0.0.

  • max_diffs (int, optional) – Maximum number of data differences recorded per HDU. Default 10. Counting continues past this limit. The true count appears in the text report. Header differences are never truncated.

  • ignore_keywords (sequence of str, optional) – Header keywords to ignore, case-insensitive. Default ["CHECKSUM", "DATASUM", "DATE"].

Returns:

FitsDiff – The comparison result. Call str(diff) to get the text report. FitsDiff.identical is True when the files match.

Raises:

FitsError – If either file cannot be opened or parsed as FITS.

Notes

fitsy combines the two tolerances as |a - b| <= atol + rtol * |b|. Both default to 0.0, which requires exact equality. A relative tolerance alone cannot reconcile values that straddle zero. Two values that are both NaN compare equal at any tolerance.

The tolerances apply to every floating-point value fitsy compares: header card values, image pixels, and table cells. fitsy compares image pixels in physical units, with BZERO and BSCALE applied and BLANK as NaN. It reports an image difference by pixel number and a table difference as COLUMN[row].

A random-groups HDU, or an HDU of an extension type fitsy does not recognize, has no decoded form. fitsy compares its raw bytes instead. That comparison does not use rtol or atol.

When the two HDUs at one index have different types, fitsy reports the type difference and compares neither the headers nor the data of that HDU.

class fitsy.FitsDiff

Bases: object

Result of comparing two FITS files. See diff().

__bool__()

True if self else False

__str__()

Return str(self).

diff_hdu_count

Number of compared HDUs with a reported difference.

diff_hdu_indices()

Return the index of every compared HDU with a difference.

Returns:

list of int – Indices in ascending order. An index counts from the primary HDU, which is index 0.

hdu_counts

HDU counts of the two files, as (n_a, n_b). When the counts differ, only the shared prefix of HDUs is compared.

identical

True when both files have the same number of HDUs and no compared HDU has a reported difference. Differences are judged with the tolerances and ignored keywords passed to diff().

report()

Render the differences as text.

Returns:

str – A multi-line report. The report names each HDU with a difference, then lists the header differences and the data differences of that HDU. str(diff) returns the same text.