Header objects¶
- class rustfits.FITSHeader¶
Bases:
objectLive view of an HDU’s FITS header.
Returned by
hdu.header(seeHDU.header). Behaves like a mapping for lookup (header["KEY"]/"KEY" in header/header.get("KEY", default)) and for mutation (header["KEY"] = value/del header["KEY"]). Mutations write through to disk immediately — no flush required, no two-step commit.Reading:
header[key]returns the value directly (astropy-style), not a{value, comment}dict. Per-key comments come fromcomment_of().header.to_dict()returns the legacy shape for serialization or tests.Writing: every mutation follows disk-write-before-commit: the file is updated first, and only on success is the in-memory card list replaced. Pre-I/O errors (e.g. a header that would overflow its reserved blocks AND the grow path is disabled — currently rare; in-place file grow is supported) leave the header unchanged. Mid-write I/O failures taint the file (close + reopen to recover).
Protected keys. Some keywords represent state rustfits manages on the user’s behalf — file structure (
SIMPLE,BITPIX,NAXIS*,XTENSION,PCOUNT,GCOUNT,TFIELDS,TFORMn, …), integrity (CHECKSUM,DATASUM), or compression layout (ZIMAGE,ZCMPTYPE,ZBITPIX,ZNAXIS*, …).__setitem__and__delitem__raiseValueErroron these keys. Use the dedicated HDU APIs for structural changes (e.g.ImageHDU.extend(),TableHDU.insert_column()).Batched edits. For multiple mutations, prefer
edit()(returns aFITSHeaderEditcontext manager) so the file is rewritten once at the end rather than per mutation.Examples
Lookup and iteration:
value = header["EXPTIME"] for k in header: print(k, header[k]) if "FILTER" in header: ...
Single mutation:
header["EXPTIME"] = 30.0 header["COMMENT"] = "calibration frame" # via add_comment del header["JUNK"]
Batched mutation:
with header.edit() as h: h["A"] = 1 h["B"] = 2 h["C"] = 3 # exactly one disk rewrite at the end
Bulk copy from another header or a dict via
update():# Copy metadata from one HDU's header to another. # Protected (structural / integrity / compression) keys # in the source are skipped silently; commentary cards # are skipped unless copy_commentary=True. fits[2].header.update(fits[1].header) # From a dict (every key must be non-protected; whole # update is rejected if any key is protected). fits[1].header.update({"OBSERVER": "Hubble", "EXPTIME": 30.0})
- __contains__(key, /)¶
Return bool(key in self).
- __delitem__(key, /)¶
Delete self[key].
- __getitem__(key, /)¶
Return self[key].
- __iter__()¶
Implement iter(self).
- __len__()¶
Return len(self).
- __setitem__(key, value, /)¶
Set self[key] to value.
- add_blank(text)¶
Append a blank-keyword commentary card to the header.
“Blank” cards have an all-spaces keyword; FITS uses them for visual section separators and unstructured notes. Same append-only semantics as
add_comment().
- add_comment(text)¶
Append a
COMMENTcard to the header.Long text auto-splits across multiple commentary cards (FITS commentary cards hold ~72 chars each). Always appends; the
COMMENTkeyword is not a single-valued card. Usedel header["COMMENT"]to remove every COMMENT card at once.
- add_history(text)¶
Append a
HISTORYcard to the header.HISTORY is FITS’s audit-trail keyword — each pipeline-processing step typically appends one card. Same append-only semantics as
add_comment().
- cards¶
The raw 80-character header card strings.
Useful for low-level inspection or for tools that need to write the bytes themselves. Returns a snapshot — the list is a copy, mutations to it don’t write back.
- comment_of(key)¶
Return the comment text attached to a header card.
FITS cards have an optional
/ commentfield after the value. This accessor returns that text (empty string when no comment is present). Use this rather than parsing card text yourself.- Parameters:
key (str) – Keyword name. Case-insensitive. Not valid for commentary keys (
COMMENT/HISTORY/ blank); for those,header[key]returns the list of commentary texts directly.- Raises:
KeyError –
keyis absent from the header.ValueError –
keyis a commentary keyword.
- edit()¶
Open a batched-edit context.
Returns a
FITSHeaderEditcontext manager. Mutations inside thewithblock accumulate in memory; at__exit__(with no exception) they’re committed to disk in a single header rewrite. This is the right shape for multiple mutations:header.edit()does one I/O instead of one per mutation.Example
with header.edit() as h: h["EXPTIME"] = 30.0 h["FILTER"] = "g" h["GAIN"] = 1.5
- get(key, default=None)¶
Get the value of
key, ordefaultif absent.Mapping-style accessor — same shape as Python’s
dict.get. Useful when you don’t want aKeyErroron missing keys.- Parameters:
key (str) – Keyword name. Case-insensitive.
default (Any, optional) – Value to return when
keyis absent. Defaults toNone.
- keys()¶
List of unique keyword names in on-disk order.
Commentary keywords (
COMMENT,HISTORY, blank) appear at most once each, even if there are multiple commentary cards.
- to_dict(skip_protected=False)¶
Snapshot the header as a
{key: {value, comment}}dict.Legacy shape, useful for serialization and tests. Everyday code should prefer
header[key](returns the value directly) andcomment_of()(returns the per-key comment string).- Parameters:
skip_protected (bool, optional) – If
True, omit protected keywords (structural, integrity, compression) from the output — produces a dict suitable for passing back intoupdate()on another header. DefaultFalse(everything included).- Returns:
{key: {"value": ..., "comment": ...}}for regular keys, and{key: [text, text, ...]}for the commentary keysCOMMENT/HISTORY/ blank.- Return type:
- update(other, *, copy_commentary=False)¶
Bulk-copy entries from another header or a dict.
- Parameters:
other (FITSHeader or dict) – Source of the new entries. When a
FITSHeader, protected keywords are silently skipped (the destination already has its own correct structural / integrity / compression keys, and they must not be clobbered). When a dict, protected keywords raise — an explicit hand-written{"BITPIX": 32}in user code is almost certainly a mistake.copy_commentary (bool, optional, keyword-only) – Only meaningful for
FITSHeadersources. IfTrue, commentary cards (COMMENT/HISTORY/ blank) are appended verbatim; one append per source card, no deduplication. DefaultFalse(commentary skipped — the common case is “copy structured metadata from this other header”). For dict sources, the flag is ignored and commentary keywords always raise.
- Raises:
ValueError – dict source contains a protected key, or (regardless of
copy_commentary) a dict source contains a commentary key. The whole update is rejected — no partial commit.
Notes
All entries are written to disk in a single header rewrite (one I/O), not per-key. Validate-then-mutate: validation errors leave the file untouched.
- class rustfits.FITSHeaderEdit¶
Bases:
objectA transactional batch of header mutations.
Returned by
FITSHeader.edit()— meant to be used as a context manager. Mutations inside thewithblock accumulate in memory; on clean exit, the accumulated card list is written to disk in a single header rewrite. If the block exits with an exception, no commit happens — the header on disk is unchanged.This is the right shape for multiple mutations in a row. Each mutation through
FITSHeaderdirectly does its own disk write; withFITSHeaderEdit, you pay one I/O for the whole batch.The mutation surface (
__setitem__/__delitem__/add_comment()/add_history()/add_blank()/update()) mirrorsFITSHeader’s exactly. Read accessors (__getitem__/__contains__/__repr__) reflect the in-progress staged state, not the on-disk header.Examples
with header.edit() as h: h["A"] = 1 h["B"] = 2 del h["JUNK"] h.add_history("Reprocessed with pipeline v2.3") # exactly one disk rewrite happens here, at __exit__
Notes
Calling mutation methods outside a
withblock raises. Re-using aFITSHeaderEditafter commit raises; open a new one with anotherFITSHeader.edit()call.- __contains__(key, /)¶
Return bool(key in self).
- __delitem__(key, /)¶
Delete self[key].
- __getitem__(key, /)¶
Return self[key].
- __setitem__(key, value, /)¶
Set self[key] to value.
- add_blank(text)¶
Append a blank-keyword commentary card to the staged edits. See
FITSHeader.add_blank().
- add_comment(text)¶
Append a
COMMENTcard to the staged edits.Same semantics as
FITSHeader.add_comment(); the commentary is held in memory until the surroundingwithblock exits.
- add_history(text)¶
Append a
HISTORYcard to the staged edits. SeeFITSHeader.add_history().
- update(other, *, copy_commentary=False)¶
Bulk-copy entries from another header or a dict into the staged edits.
Same shape, source rules, and
copy_commentarysemantics asFITSHeader.update(); the difference is that the entries go into the staged-but-uncommitted card list and write to disk at__exit__.
- rustfits.is_protected_key(key)¶
Check if the input keyword is protected
Is this (post-normalization) key one that rustfits manages on the user’s behalf — i.e., one whose value is determined by the file’s structure, integrity contract, or compression layout, and which the user must NOT mutate directly?
Categories: image-HDU structural, binary/ASCII table structural, random groups, tiled image compression, integrity (CHECKSUM/DATASUM). Not protected: user metadata like OBJECT, EXPTIME, EXTNAME, BUNIT, BSCALE, BZERO, CTYPEn, CRVALn, etc.
- Parameters:
key (str) – A keyword name
- Return type:
True if it is protected, False otherwise