Reading FITS files¶
- fitsy.open(path, mode='readonly', lenient=False)¶
Open a FITS file by path.
- Parameters:
path (str or os.PathLike) – Filesystem path to the FITS file.
mode ({'readonly', 'update'}, optional) –
'readonly'(default) opens read-only; header mutations andFitsFile.writeto()raiseValueError. Matches astropy’sfits.openmode of the same name.'update'opens read/write. Header edits and image-pixel in-place edits (hdu.data[...] = x) are preserved onFitsFile.writeto(). Table column data is read-only in this release; reconstruct the table withfitsy.bintable()to change column values.lenient (bool, optional) – When True, accept
SIMPLE = Fprimary headers (non-standard FITS files written by some pipelines). Does not downgrade any other validation errors to warnings. Default False.
- Returns:
FitsFile – A read-only or read/write handle depending on
mode.- Raises:
ValueError – If
modeis 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:
objectOwning, ordered, mutable list of HDUs.
astropy parity:
FitsFilebehaves like astropy’sastropy.io.fits.HDUList. Slots are typed Python objects (ImageHdu/BinTable/AsciiTable); they own their header and data and survive after the file handle is dropped.In
mode='readonly'(the default), in-place edits (f[0].data[...] = x,f[0].header["K"] = v,f.append(hdu),del f[i]) are kept in memory and preserved on the nextwriteto(); the on-disk source file is never modified.In
mode='update', those same edits are written back to the source file onflush(),close(), or clean__exit__. Pixel patches viaf[i].section[...] = arrare written immediately via positionalpwrite.Use the module-level
open()factory rather than constructing this class directly.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[0].header["OBSERVER"] = "you" ... f.writeto("edited.fits") # original untouched
- __enter__()¶
Context-manager entry. Returns
self.
- __exit__(exc_type=None, _exc_val=None, _exc_tb=None)¶
- __getitem__(key, /)¶
Return self[key].
- __len__()¶
Return len(self).
- add_checksums()¶
Enable
CHECKSUM/DATASUMstamping on the nextwriteto()orflush().When called, every HDU emitted by the next write will gain freshly computed
CHECKSUMandDATASUMcards (per the FITS Checksum Proposal). Existing placeholder cards in each header are overwritten in place; missing ones are inserted. The flag stays on for the lifetime of theFitsFileobject, matching astropy’s semantics wherehdu.add_checksum()permanently mutates the HDU.Notes
This does not stamp anything immediately – the actual computation happens during the next write, when the final byte layout of each HDU is known. To verify checksums on the resulting file, call
verify_checksums()after the write.Astropy parity: equivalent to calling
hdu.add_checksum()on every HDU in the list. There is no per-HDU variant in fitsy because checksums must be computed against the final on-disk byte layout.
- append(value)¶
Append an HDU at the end. Accepts an HDU instance or a builder.
- close()¶
Flush pending edits (if any) and release the source file handle.
After
close()the slot list and any HDU wrappers Python already holds remain usable as in-memory data, but the underlying file handle is dropped so anyPendingslot that has not yet been materialized will fail to load. Subsequentflush()/writeto()calls also raise.Idempotent: calling
close()more than once is safe. Astropy parity: matchesHDUList.close().
- flush()¶
Flush pending edits to disk.
f.append(...),del f[i], fancy / dtype-mismatched patches, edits on a tile-compressed image), rewrites the file via a sibling temp file + atomicrename. Slots the user never touched are streamed byte-for-byte from the original file (no decode/re-encode).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 (viapwrite) and the subsequentflush()then performs a full rewrite that includes the patched bytes by streaming the (already patched) source file. Patches are not lost.Crash safety: in-place patches use
pwritewith no undo journal; a process death mid-patch can leave the file with some rows updated and others not (this matches astropy’s mmap-backed update mode). The full-rewrite path is crash-safe because it writes to a sibling temp file and renames atomically once the bytes are durable. Note that the parent directory is not separatelyfsynced, so a power loss between rename and the next directory commit can theoretically leave the rename invisible after reboot on non-journaling filesystems. Stale.fitsy-tmp.*siblings from a crashed rewrite are harmless and may be deleted.A no-op for read-only files.
- hdu(i)¶
Return the
i-th HDU. Equivalent tofile[i]for non-negative integeri.
- hdu_by_name(name, ver=None)¶
Return the first HDU with matching
EXTNAME.- Parameters:
- Raises:
IndexError – If no HDU matches.
- insert(i, value)¶
Insert an HDU at position
i. Accepts an HDU instance or a builder.
- read_only¶
True when the file was opened read-only.
- verify_checksums()¶
Verify per-HDU
CHECKSUMandDATASUMcards.Streams the data section of each HDU directly from disk in fixed-size chunks (no full materialisation) 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 fieldNoneand the present one asTrue/False.- Returns:
list[dict] – One dict per HDU, in file order. Keys:
hdu– 0-based HDU index (int).checksum_ok–True/False/None.datasum_ok–True/False/None.
Notes
Works on any file (read-only or update), and on in-memory FitsFile objects whose data is already resident. Astropy parity: equivalent to iterating
[hdu.verify_checksum() for hdu in hdul]but without reading the data into memory.
- wcs(i=0, alt=' ')¶
Resolve the WCS for the given HDU index.
- Parameters:
Notes
Only the target HDU’s header is consulted;
-TABaxis tables stored in sibling HDUs are not currently resolved.
- 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 (sohdu.data[...] = xround-trips);BITPIXandNAXIS*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).
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
pathresolves to the same file the handle was opened from – a self-write requires update mode and triggers an in-place rewrite (alias forflush()).- Parameters:
path (str or os.PathLike) – Destination path.
overwrite (bool, optional) – If False (default), raise
FileExistsErrorwhenpathalready exists. Set to True to replace it.
- Raises:
ValueError – If the file contains zero HDUs, or if
pathresolves to the source file and the handle is read-only.FileExistsError – If
pathexists andoverwriteis False.FitsError – On I/O failure.
- class fitsy.ImageHdu(data, header=None, name=None)¶
Bases:
objectImage HDU with lazy numpy data.
Returned by
FitsFile.hdu()(orfile[i]) when the HDU kind is an image. The pixel data is not read at materialisation time; the first access tohdu.data(or any operation that needs the full array, e.g.FitsFile.writeto()) reads it from disk via positionalpread. Subsequent accesses return the same array, and in-place mutation (hdu.data[0, 0] = 42) is preserved on the nextFitsFile.writeto().For workloads that operate on rectangular sub-regions of an image larger than RAM, use
section–hdu.section[a:b]reads only the requested bytes, andhdu.section[a:b] = arrwrites only the touched bytes (viapwrite) without materialising the full array in memory.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 materialised, 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
BITPIXvalue (e.g.-32forf32).
- data¶
Pixel data as a numpy array.
Materialises the array on first access by reading the data section from disk, byteswapping into native order, and applying
BSCALE/BZERO/BLANKscaling. Subsequent accesses return the same array, and in-place mutation (hdu.data[...] = x) is preserved by the nextFitsFile.writeto().For images that do not fit in RAM, prefer
section–hdu.section[a:b]reads only the requested bytes without materialising the full array.- Returns:
numpy.ndarray or None –
Nonewhen the HDU has no data section (NAXIS == 0).
- section¶
Slicing accessor that mirrors
numpy.ndarrayindexing.hdu.section[a:b, c:d]reads only the requested region from disk – no full-image materialisation.In
mode='update',hdu.section[a:b] = arrwrites only the touched bytes back via positionalpwrite, again without materialising 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:stopwith step 1) on an HDU with identity scaling (BSCALE=1,BZERO=0, noBLANK). Anything else (fancy indexing, negative steps, scaled HDUs) raises aValueError– assign throughhdu.data[...]to trigger a full-file rewrite instead.If
hdu.datahas already been accessed (and is therefore resident in memory), reads and writes go through the in-memory array for consistency with subsequenthdu.dataaccesses.- Returns:
_ImageSection – Slicing proxy. Use
section[i, j, k]exactly likedata[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 None –
Noneif the header carries no WCS foralt.
Notes
Only this HDU’s header is consulted;
-TABaxis tables stored in sibling HDUs are not resolved.
- class fitsy.BinTable¶
Bases:
objectBinary table HDU (
BINTABLE).Returned by
FitsFile.hdu()(orfile[i]) when the HDU kind isBINTABLE. Columns are decoded eagerly:Scalar numeric columns return 1-D
numpy.ndarray.Fixed-repeat columns return
(n_rows, repeat)2-D arrays.Variable-length and string columns return Python lists.
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].
- column_names¶
List of column names in declaration order.
- data¶
Pre-decoded columns assembled into a numpy structured array (one record per row, dtype follows column types).
Variable-length and string columns are exposed via
objectdtype; numeric scalars retain native dtype. Returned array is read-only.
- header¶
The HDU header.
- n_rows¶
Number of rows in the table.
- row(r)¶
Build a row dict for row index r.
- to_dict()¶
Materialise every column as a plain
dict[str, ndarray | list].
- class fitsy.AsciiTable¶
Bases:
objectASCII
TABLEHDU.Returned by
FitsFile.hdu()when the HDU kind isTABLE. Columns where every cell parses as numeric are returned asnumpy.ndarray(withNaNfor null cells); mixed columns fall back tolist[str].- __getitem__(key, /)¶
Return self[key].
- column(name)¶
Column accessor; equivalent to
table[name].
- column_names¶
List of column names in declaration order.
- data¶
All columns assembled into a numpy structured array.
- header¶
The HDU header.
- n_rows¶
Number of rows in the table.
- row(r)¶
Build a row dict for row index r.
- to_dict()¶
Materialise every column as a plain
dict[str, ndarray | list].
- class fitsy.RandomGroups¶
Bases:
objectRandom-groups primary HDU (legacy format; see Standard Sec.6).
Read-only Python view: groups are decoded on demand through
group(). The HDU does not participate inFitsFile.writeto(); round-tripping random-groups files through Python is intentionally not supported (use the Rust API).- bitpix¶
BITPIX value.
- data_per_group¶
Number of data values per group (prod(NAXIS2..NAXISn)).
- group(i)¶
Decode group i (0-based) as (parameters, data) numpy arrays. Both arrays use the HDU’s BITPIX dtype; BSCALE, BZERO, PSCALn, PZEROn are not applied.
- 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.
- fitsy.getheader(path, ext=None)¶
Read one HDU’s header from path.
- fitsy.getval(path, key, ext=None)¶
Read one header keyword from path.
Raises KeyError if the keyword is absent.
- fitsy.info(path)¶
Return a brief HDU summary table for path.
Returns a list of (index, name, ver, kind, dims_or_n_rows) tuples. kind is the wrapper class name (“ImageHdu”, “BinTable”, …).
Comparing files¶
- fitsy.diff(a, b, *, rtol=0.0, max_diffs=10, ignore_keywords=None)¶
Compare two FITS files and return a diff report.
- Parameters:
a (str | os.PathLike) – Paths to the two files to compare.
b (str | os.PathLike) – Paths to the two files to compare.
rtol (float, optional) – Relative tolerance for floating-point comparisons. Default
0.0(exact equality).max_diffs (int, optional) – Maximum number of differences recorded per category before truncation. Default 10.
ignore_keywords (list[str], optional) – Header keywords to ignore (case-insensitive). Defaults to
["CHECKSUM", "DATASUM", "DATE"].
- Returns:
FitsDiff – Diff object. Use
str(diff)for the report;diff.identicalis True when the files match.
- class fitsy.FitsDiff¶
Bases:
objectResult of comparing two FITS files.
- __bool__()¶
True if self else False
- __str__()¶
Return str(self).
- diff_hdu_count¶
Number of HDUs that have at least one difference.
- diff_hdu_indices()¶
List of HDU indices that contain differences.
- hdu_counts¶
(n_a, n_b)HDU counts.
- identical¶
True when both files have the same number of HDUs and every HDU is byte-equivalent under the configured options.
- report()¶
Multi-line text report (mirrors astropy
FITSDiff.report).