zigfitsio 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zigfitsio-0.1.1/.gitignore +17 -0
- zigfitsio-0.1.1/CHANGELOG.md +140 -0
- zigfitsio-0.1.1/LICENSE +21 -0
- zigfitsio-0.1.1/PKG-INFO +145 -0
- zigfitsio-0.1.1/README.md +140 -0
- zigfitsio-0.1.1/bindings/c/zigfitsio.h +226 -0
- zigfitsio-0.1.1/bindings/capi/abi.zig +264 -0
- zigfitsio-0.1.1/bindings/capi/capi.zig +1423 -0
- zigfitsio-0.1.1/bindings/capi/test_capi.zig +798 -0
- zigfitsio-0.1.1/bindings/python/README.md +119 -0
- zigfitsio-0.1.1/bindings/python/ci/install_zig.sh +42 -0
- zigfitsio-0.1.1/bindings/python/examples/quickstart.py +63 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/__init__.py +78 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/_dtypes.py +119 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/_loader.py +61 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/core.py +1226 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/header.py +252 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/lowlevel.py +328 -0
- zigfitsio-0.1.1/bindings/python/src/zigfitsio/py.typed +0 -0
- zigfitsio-0.1.1/bindings/python/tests/conftest.py +45 -0
- zigfitsio-0.1.1/bindings/python/tests/test_api_surface.py +371 -0
- zigfitsio-0.1.1/bindings/python/tests/test_astropy_xcheck.py +86 -0
- zigfitsio-0.1.1/bindings/python/tests/test_bugfixes.py +434 -0
- zigfitsio-0.1.1/bindings/python/tests/test_golden.py +60 -0
- zigfitsio-0.1.1/bindings/python/tests/test_highlevel.py +132 -0
- zigfitsio-0.1.1/bindings/python/tests/test_lowlevel.py +41 -0
- zigfitsio-0.1.1/build.zig +198 -0
- zigfitsio-0.1.1/build.zig.zon +19 -0
- zigfitsio-0.1.1/hatch_build.py +131 -0
- zigfitsio-0.1.1/interop/README.md +66 -0
- zigfitsio-0.1.1/pyproject.toml +87 -0
- zigfitsio-0.1.1/src/checksum.zig +575 -0
- zigfitsio-0.1.1/src/compress/dither.zig +369 -0
- zigfitsio-0.1.1/src/compress/gzip.zig +123 -0
- zigfitsio-0.1.1/src/compress/hcompress.zig +1343 -0
- zigfitsio-0.1.1/src/compress/plio.zig +489 -0
- zigfitsio-0.1.1/src/compress/rice.zig +438 -0
- zigfitsio-0.1.1/src/compress/shuffle.zig +107 -0
- zigfitsio-0.1.1/src/compress/tiled.zig +2702 -0
- zigfitsio-0.1.1/src/convert.zig +147 -0
- zigfitsio-0.1.1/src/diag.zig +97 -0
- zigfitsio-0.1.1/src/endian.zig +112 -0
- zigfitsio-0.1.1/src/errors.zig +191 -0
- zigfitsio-0.1.1/src/filename.zig +631 -0
- zigfitsio-0.1.1/src/fits.zig +1425 -0
- zigfitsio-0.1.1/src/group_table.zig +759 -0
- zigfitsio-0.1.1/src/groups.zig +578 -0
- zigfitsio-0.1.1/src/hdu.zig +572 -0
- zigfitsio-0.1.1/src/header/card.zig +243 -0
- zigfitsio-0.1.1/src/header/continue.zig +244 -0
- zigfitsio-0.1.1/src/header/header.zig +715 -0
- zigfitsio-0.1.1/src/header/hierarch.zig +203 -0
- zigfitsio-0.1.1/src/header/name.zig +362 -0
- zigfitsio-0.1.1/src/header/value.zig +544 -0
- zigfitsio-0.1.1/src/image.zig +1027 -0
- zigfitsio-0.1.1/src/io/block.zig +273 -0
- zigfitsio-0.1.1/src/io/device.zig +155 -0
- zigfitsio-0.1.1/src/io/file.zig +157 -0
- zigfitsio-0.1.1/src/io/http.zig +288 -0
- zigfitsio-0.1.1/src/io/memory.zig +144 -0
- zigfitsio-0.1.1/src/io/stream.zig +308 -0
- zigfitsio-0.1.1/src/iterator.zig +789 -0
- zigfitsio-0.1.1/src/limits.zig +95 -0
- zigfitsio-0.1.1/src/root.zig +232 -0
- zigfitsio-0.1.1/src/table/ascii.zig +988 -0
- zigfitsio-0.1.1/src/table/binary.zig +1951 -0
- zigfitsio-0.1.1/src/table/common.zig +609 -0
- zigfitsio-0.1.1/src/table/heap.zig +1475 -0
- zigfitsio-0.1.1/src/template.zig +562 -0
- zigfitsio-0.1.1/src/validate.zig +996 -0
- zigfitsio-0.1.1/src/version.zig +97 -0
- zigfitsio-0.1.1/src/wasm_check.zig +70 -0
- zigfitsio-0.1.1/src/wcs/celestial.zig +556 -0
- zigfitsio-0.1.1/src/wcs/keys.zig +538 -0
- zigfitsio-0.1.1/src/wcs/spectral.zig +261 -0
- zigfitsio-0.1.1/src/wcs/time.zig +793 -0
- zigfitsio-0.1.1/test/corpus/ascii_table.fits +1 -0
- zigfitsio-0.1.1/test/corpus/binary_table.fits +0 -0
- zigfitsio-0.1.1/test/corpus/binary_table_vla.fits +0 -0
- zigfitsio-0.1.1/test/corpus/compressed_image.fits.gz +0 -0
- zigfitsio-0.1.1/test/corpus/image_extension.fits +0 -0
- zigfitsio-0.1.1/test/corpus/primary_image.fits +0 -0
- zigfitsio-0.1.1/test/corpus.zig +379 -0
- zigfitsio-0.1.1/test/e2e.zig +1009 -0
- zigfitsio-0.1.1/test/fuzz/fuzz.zig +141 -0
- zigfitsio-0.1.1/test/golden/.gitattributes +7 -0
- zigfitsio-0.1.1/test/golden/MANIFEST.json +109 -0
- zigfitsio-0.1.1/test/golden/README.md +16 -0
- zigfitsio-0.1.1/test/golden/checksum/cfitsio_ascii_checksum.fits +1 -0
- zigfitsio-0.1.1/test/golden/compress/tile_gzip.fits +0 -0
- zigfitsio-0.1.1/test/golden/compress/tile_hcompress.fits +0 -0
- zigfitsio-0.1.1/test/golden/compress/tile_plio.fits +0 -0
- zigfitsio-0.1.1/test/golden/compress/tile_rice.fits +0 -0
- zigfitsio-0.1.1/test/golden/conformance/expected.json +15 -0
- zigfitsio-0.1.1/test/golden/conformance/malformed/blank_on_float.fits +0 -0
- zigfitsio-0.1.1/test/golden/conformance/valid/image.fits +0 -0
- zigfitsio-0.1.1/test/golden/images/img_f32.fits +0 -0
- zigfitsio-0.1.1/test/golden/images/img_i16.fits +0 -0
- zigfitsio-0.1.1/test/golden/tables/ascii.fits +1 -0
- zigfitsio-0.1.1/test/golden/tables/bintable.fits +0 -0
- zigfitsio-0.1.1/test/golden/wcs/wcs_refpoints.csv +6 -0
- zigfitsio-0.1.1/test/golden/wcs/wcs_tan.fits +0 -0
- zigfitsio-0.1.1/test/golden.zig +334 -0
- zigfitsio-0.1.1/tools/bench.zig +78 -0
- zigfitsio-0.1.1/tools/emit_fixtures.zig +158 -0
- zigfitsio-0.1.1/tools/fitsverify.zig +55 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.zig-cache/
|
|
2
|
+
zig-out/
|
|
3
|
+
*.o
|
|
4
|
+
.claude/
|
|
5
|
+
|
|
6
|
+
# Python bindings
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*.egg-info/
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.venv/
|
|
14
|
+
# Zig-built shared library staged into the package by hatch_build.py
|
|
15
|
+
bindings/python/src/zigfitsio/*.so
|
|
16
|
+
bindings/python/src/zigfitsio/*.dylib
|
|
17
|
+
bindings/python/src/zigfitsio/*.dll
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `zigfitsio` are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) (`NFR-API-1`).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
_Nothing yet._
|
|
10
|
+
|
|
11
|
+
## [0.1.1] - 2026-07-02
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
A swarm bug hunt over the ctypes bindings + C-ABI boundary fixed 27 confirmed defects (the prior
|
|
15
|
+
suite exercised none of these paths; `bindings/python/tests/test_bugfixes.py` now pins them):
|
|
16
|
+
- **Critical:** `open()` → `writeto()`/`to_bytes()` no longer silently drops image/table data — an
|
|
17
|
+
attached HDU list is copied byte-faithfully (raw device passthrough), and the reconstruction path
|
|
18
|
+
serializes materialized data for detached/mixed lists.
|
|
19
|
+
- **Crashes:** using an attached HDU after `close()`, and `verify()` on a Python-built HDU list, now
|
|
20
|
+
raise `FitsError` instead of segfaulting; the C-ABI `zf_*` exports null-check their handle
|
|
21
|
+
argument (`NULL_INPUT_PTR`, 104).
|
|
22
|
+
- **Silent corruption:** non-native-endian arrays are coerced to native before write; numpy scalar
|
|
23
|
+
header values (`np.int64/np.float32/np.bool_`) are written with the correct FITS type; out-of-range
|
|
24
|
+
integer keywords raise instead of wrapping; non-ASCII table strings and ragged/empty tables raise.
|
|
25
|
+
- **Reads:** binary-table columns honor `TSCAL`/`TZERO` (fractional scaling → float; unsigned
|
|
26
|
+
`TZEROn` → `u2/u4/u8`, no more overflow); VLA columns decode with their real element type (not
|
|
27
|
+
always `float64`); long-string `CONTINUE` values and `HIERARCH` keywords are parsed.
|
|
28
|
+
- **Writes (feature completion):** unsigned `u2/u4/u8` images and columns, and variable-length-array
|
|
29
|
+
columns, are now writable; update-mode image data edits are written back on `flush()`.
|
|
30
|
+
- **API/packaging:** `getdata` falls through an empty primary and `hdul['PRIMARY']` resolves
|
|
31
|
+
(astropy parity); the wheel build hook and dev loader find the Windows DLL under `zig-out/bin` and
|
|
32
|
+
derive the library name + wheel tag from `ZIG_TARGET`; image element-count ABI widened to 64-bit.
|
|
33
|
+
|
|
34
|
+
Core-library and build fixes in the same window:
|
|
35
|
+
- **WCS:** `altSuffix` no longer returns a dangling stack temporary — alternate-WCS keyword
|
|
36
|
+
lookups misbehaved in ReleaseFast builds (Debug's stack layout masked it).
|
|
37
|
+
- **I/O:** `appendHdu` rolls back device growth when it fails after the header write; `sync` on a
|
|
38
|
+
read-only file device is a no-op (Windows `FlushFileBuffers` fails on read-only handles, which
|
|
39
|
+
POSIX `fsync` had masked).
|
|
40
|
+
- **Packaging/CI:** wheel builds fixed on Windows (skip 32-bit `*-win32`) and macOS (pin the 11.0
|
|
41
|
+
deployment target); the retired `macos-13` runner replaced with `macos-15-intel`.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- `zf_create_tbl_heap` C-ABI entry point (reserves `PCOUNT` heap for VLA writes).
|
|
45
|
+
|
|
46
|
+
## [0.1.0] - 2026-06-30
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- Build scaffolding: `zig build` (static library), `test`, `bench`, `fuzz`, and
|
|
50
|
+
`wasm-check` steps; dependency-free `build.zig.zon` (`SETUP-1`).
|
|
51
|
+
- MIT license; README usage examples and changelog (`X-DOC`).
|
|
52
|
+
- **M0 foundation:** error sets + CFITSIO status map, diagnostics, version/`errorText`,
|
|
53
|
+
resource limits with validate-before-allocate, big-endian access, numeric-conversion policy.
|
|
54
|
+
- **I/O layer:** `Device` vtable; in-memory, file, and stream backends; 2880-byte block
|
|
55
|
+
buffering with the correct pad kinds.
|
|
56
|
+
- **Header layer:** keyword name normalization + wildcard matching, value parsing (the
|
|
57
|
+
null/empty/undefined distinction, `CONTINUE`, `HIERARCH`), 80-byte cards, the header
|
|
58
|
+
container with read + full edit operations and header-space pre-allocation.
|
|
59
|
+
- **HDU model & `Fits` handle:** kind detection, mandatory-keyword validation, lazy HDU scan,
|
|
60
|
+
navigation, and the programmatic image/HDU builders.
|
|
61
|
+
- **HDU editing (`FITS-1b`):** a block-aligned data-resize + following-HDU shift primitive
|
|
62
|
+
(`resizeHduData`/`refreshGeometry`/`rewriteHeaderInPlace`), plus `deleteHdu`/`copyHdu`.
|
|
63
|
+
- **Images:** `ImageView` over all six `BITPIX`; full/contiguous/strided-section pixel I/O;
|
|
64
|
+
`BSCALE`/`BZERO` scaling; unsigned-integer convention; `BLANK`/NaN nulls; `reshape` (`IMG-7`).
|
|
65
|
+
- **Tables:** ASCII and binary tables (all `TFORM` codes, scaling, nulls, `A`-format,
|
|
66
|
+
`TDIM`), variable-length arrays with a compacting heap; binary-table row/column structural
|
|
67
|
+
operations — append/insert/delete/copy rows and columns (`BTB-3b`).
|
|
68
|
+
- **Integrity:** `DATASUM`/`CHECKSUM` compute/update/verify.
|
|
69
|
+
- **WCS:** keyword set parse/serialize; celestial transforms (zenithal family + `CAR`);
|
|
70
|
+
spectral and time-coordinate keywords.
|
|
71
|
+
- **Compression:** GZIP_1/GZIP_2 codecs and the type-aware byte shuffle; RICE_1, PLIO_1, and
|
|
72
|
+
HCOMPRESS_1 tile codecs; subtractive dithering with the Park–Miller generator; a tiled
|
|
73
|
+
compressed-image **write** path; and tile-compressed-table (`ZTABLE`) reading.
|
|
74
|
+
- **Extended I/O & convenience:** CFITSIO-style extended-filename DSL → `FileSpec` (`EFN-1`);
|
|
75
|
+
whole-file gzip backend (`RMT-1`); ASCII header-template loader (`TPL-1`); hierarchical
|
|
76
|
+
grouping tables, read and edit (`GRP-1`).
|
|
77
|
+
- **Utilities:** date/time + Julian-Date helpers; `TFORM`/`TDISP` parsing.
|
|
78
|
+
- **Cross-cutting:** fuzz harnesses for the parsers; a CI portability matrix
|
|
79
|
+
(incl. a big-endian QEMU cell and a wasm32-freestanding build); real bulk-image throughput
|
|
80
|
+
benchmarks (`X-BENCH`); and the **full** upper-layer stack now compiles for
|
|
81
|
+
wasm32-freestanding (`X-WASM`).
|
|
82
|
+
- **HTTP(S) range-GET backend (`RMT-2`):** `src/io/http.zig`'s `HttpDevice` serves a remote
|
|
83
|
+
FITS file as a seekable read-only `Device` via Range GETs, falling back to a full in-memory
|
|
84
|
+
download when the server lacks range support; excluded from the freestanding build graph.
|
|
85
|
+
- A transparent `.fits.gz` open path and a committed sample FITS corpus.
|
|
86
|
+
- **In-house end-to-end harness (`test/e2e.zig`):** a CFITSIO `testprog.c`-equivalent that builds a
|
|
87
|
+
maximal multi-HDU file exercising every `BITPIX`/`TFORM`, all four tile codecs, VLA, WCS,
|
|
88
|
+
`CONTINUE`/`HIERARCH`, and checksums, then reopens and asserts — plus a deterministic
|
|
89
|
+
byte-snapshot regression tripwire. New `zig build e2e` step.
|
|
90
|
+
- **CFITSIO 4.6.4 golden corpus (`test/golden/`, `X-FIXTURES`):** reference tiles
|
|
91
|
+
(RICE/GZIP/PLIO/HCOMPRESS), a checksum vector, images, tables, WCS, and conformance fixtures,
|
|
92
|
+
authored by `fpack` + a CFITSIO C generator under `interop/` (`MANIFEST.json` with sha256),
|
|
93
|
+
consumed hermetically by `test/golden.zig` on every cell (incl. big-endian s390x).
|
|
94
|
+
- **`interop` CI job (`X-XVAL`/`X-INTEROP`/`X-CONF`):** opens every zigfitsio-written file with
|
|
95
|
+
CFITSIO `funpack`, Astropy, and `fitsverify` (`tools/emit_fixtures.zig`, `zig build
|
|
96
|
+
emit-fixtures`); path-gated and toolchain-isolated from the hermetic matrix.
|
|
97
|
+
- **C-ABI shim (`bindings/capi/`, `zig build capi`):** a dynamic library `zigfitsio_capi`
|
|
98
|
+
exporting `zf_*` symbols over the public Zig module. The comptime-generic API is monomorphized
|
|
99
|
+
behind runtime datatype codes; Zig errors map to CFITSIO-compatible status ints via
|
|
100
|
+
`errors.cfitsioStatus`, with a thread-local last-error (message + `Diagnostics` detail). Covers
|
|
101
|
+
the full surface: lifecycle (file/memory/gzip), HDU navigation + delete/copy, header read/edit,
|
|
102
|
+
images (read/write/section/reshape with scaling + nulls), binary & ASCII tables, VLAs, tiled
|
|
103
|
+
compression write, checksum, structural validation, and WCS celestial transforms. The
|
|
104
|
+
hand-written contract is `bindings/c/zigfitsio.h`; `zig build capi-test` round-trips the ABI.
|
|
105
|
+
This is **not** a CFITSIO `fits_*`/`ff*` drop-in — it is a purpose-built ABI for bindings.
|
|
106
|
+
- **Python bindings (`bindings/python/`):** a low-level `ctypes` 1:1 binding (`zigfitsio.lowlevel`)
|
|
107
|
+
with a typed `FitsError` hierarchy, and a high-level NumPy-first API (`zigfitsio`) modeled on
|
|
108
|
+
`astropy.io.fits` — `open`, `HDUList`, `Primary`/`Image`/`CompImage`/`BinTable`/`AsciiTable`
|
|
109
|
+
HDUs, `Column`, a dict-like `Header`, `getdata`/`getheader`/`getval`/`writeto`/`verify`, and
|
|
110
|
+
celestial WCS. Includes a pytest suite (low + high level, **Astropy cross-checks both
|
|
111
|
+
directions**, and the committed golden corpus), packaging (`pyproject.toml` + a hatch build hook
|
|
112
|
+
that compiles and bundles the shared library), and a cibuildwheel matrix workflow.
|
|
113
|
+
|
|
114
|
+
### Fixed
|
|
115
|
+
- **FITS-conformance correctness pass:** ASCII-table fields now space-fill (not NUL/zero) per
|
|
116
|
+
`FR-IO-2`; the `CONTINUE`/`HIERARCH`/complex-value header API is wired through
|
|
117
|
+
`getLongString`/`appendLongString`/`getHierarch`/`getComplex`; float-exponent formatting
|
|
118
|
+
follows §4.2.4 and FORTRAN-real parsing accepts `D`/`E` exponents; `TDISP` `EN`/`ES`/`G`
|
|
119
|
+
rendering; `copyHdu` rolls back cleanly on partial failure; write-path keyword-order
|
|
120
|
+
validation; WCS `CAR`/`LATPOLE` pole rotation corrected; `MJDREF` precedence + a time-keyword
|
|
121
|
+
writer and completeness fixes; image-section streaming; `TDIM` string arrays; heap-bounds
|
|
122
|
+
hardening; and grouping-table fixes.
|
|
123
|
+
- **Standard-wire-format codec rewrites:** `PLIO_1` now follows the FITS Table 38 instruction
|
|
124
|
+
set and `HCOMPRESS_1` the White-1992 quadtree wire format; tiled `ZBLANK`, transparent
|
|
125
|
+
compressed-image reads (`ImageView.of`), and the write-side codec wiring are all in place.
|
|
126
|
+
- **Iterator** null substitution.
|
|
127
|
+
- **`PLIO_1` was not CFITSIO-interoperable in either direction:** the codec omitted the IRAF/CFITSIO
|
|
128
|
+
7-word line-list header and stored `COMPRESSED_DATA` as `1PB` instead of `1PI`. Both fixed —
|
|
129
|
+
zigfitsio now reads genuine CFITSIO PLIO tiles and writes tiles `funpack`/Astropy decode to the
|
|
130
|
+
exact pixels (caught by the golden corpus).
|
|
131
|
+
- **`checksum_on_close` was a silent no-op:** the `flush` hook was declared but never registered, so
|
|
132
|
+
no `DATASUM`/`CHECKSUM` was written. Now wired (`src/fits.zig` reserves the cards at HDU-build
|
|
133
|
+
time; `src/checksum.zig` registers `updateAll`).
|
|
134
|
+
|
|
135
|
+
### Notes
|
|
136
|
+
- The HTTP(S) range-GET backend (`RMT-2`) is **done**.
|
|
137
|
+
- **Byte-exact CFITSIO 4.6.4 / Astropy golden-corpus parity is now done** (`X-FIXTURES`/`X-SUM`/
|
|
138
|
+
`X-XVAL`/`X-CONF`/`X-INTEROP`): the tile codecs and checksum are verified against committed CFITSIO
|
|
139
|
+
tiles both inbound and outbound. The one remaining codec limit is `HCOMPRESS_1` lossy `hsmooth`
|
|
140
|
+
(lossless only); see `CAVEATS.md §1`.
|
zigfitsio-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Armando Herrera
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
zigfitsio-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zigfitsio
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python bindings for zigfitsio — a pure-Zig FITS 4.0 I/O library
|
|
5
|
+
Project-URL: Repository, https://github.com/anhydrous99/zigfitsio
|
|
6
|
+
Author: zigfitsio contributors
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: FITS,astronomy,astrophysics,cfitsio,io,zig
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: numpy>=1.20
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: astropy>=5; extra == 'test'
|
|
24
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# zigfitsio (Python)
|
|
28
|
+
|
|
29
|
+
Python bindings for [**zigfitsio**](https://github.com/anhydrous99/zigfitsio) — a pure-[Zig](https://ziglang.org)
|
|
30
|
+
implementation of [FITS](https://fits.gsfc.nasa.gov) 4.0 I/O with feature parity goals against
|
|
31
|
+
CFITSIO, and **no C dependencies**.
|
|
32
|
+
|
|
33
|
+
Two layers are provided:
|
|
34
|
+
|
|
35
|
+
- **High-level**, NumPy-first API modeled on `astropy.io.fits` — `open`, `HDUList`, the HDU
|
|
36
|
+
classes, `Column`, `Header`, and the `getdata`/`getheader`/`writeto`/`verify` conveniences.
|
|
37
|
+
- **Low-level** 1:1 `ctypes` binding under `zigfitsio.lowlevel` (the C ABI from
|
|
38
|
+
`bindings/c/zigfitsio.h`) for power users.
|
|
39
|
+
|
|
40
|
+
The native code is a Zig-built shared library loaded via `ctypes`; there is **no C compiler
|
|
41
|
+
requirement** at install time when using a prebuilt wheel.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pip install zigfitsio # prebuilt wheel (recommended)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Building from source requires a Zig toolchain (supplied automatically by the `ziglang` build
|
|
50
|
+
dependency, or a system `zig` 0.16 on `PATH`).
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import numpy as np
|
|
56
|
+
import zigfitsio as zf
|
|
57
|
+
|
|
58
|
+
# Write an image
|
|
59
|
+
zf.writeto("image.fits", np.arange(12, dtype="f4").reshape(3, 4), overwrite=True)
|
|
60
|
+
|
|
61
|
+
# Read it back (NumPy array, shape (NAXIS2, NAXIS1), C-order — like astropy)
|
|
62
|
+
with zf.open("image.fits") as hdul:
|
|
63
|
+
data = hdul[0].data
|
|
64
|
+
hdr = hdul[0].header
|
|
65
|
+
print(data.shape, hdr["NAXIS1"])
|
|
66
|
+
|
|
67
|
+
# Build a binary table
|
|
68
|
+
cols = [
|
|
69
|
+
zf.Column("INDEX", "J", np.array([10, 20, 30], dtype="i4")),
|
|
70
|
+
zf.Column("FLUX", "E", np.array([1.5, 2.5, 3.5], dtype="f4"), unit="Jy"),
|
|
71
|
+
zf.Column("NAME", "8A", np.array(["alpha", "beta", "gamma"])),
|
|
72
|
+
]
|
|
73
|
+
zf.HDUList([zf.PrimaryHDU(), zf.BinTableHDU.from_columns(cols, name="EVENTS")]).writeto(
|
|
74
|
+
"table.fits", overwrite=True
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Tile-compressed image (RICE_1)
|
|
78
|
+
ramp = np.arange(256, dtype="i4").reshape(16, 16)
|
|
79
|
+
zf.HDUList([zf.PrimaryHDU(), zf.CompImageHDU(ramp, compression="RICE_1")]).writeto(
|
|
80
|
+
"comp.fits", overwrite=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Structural validation (fitsverify-style)
|
|
84
|
+
for f in zf.verify("image.fits"):
|
|
85
|
+
print(f)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Headers (dict-like)
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
with zf.open("image.fits", mode="update") as hdul:
|
|
92
|
+
h = hdul[0].header
|
|
93
|
+
h["OBSERVER"] = ("Hubble", "the observer") # value + comment
|
|
94
|
+
print(h["OBSERVER"], "/", h.comment_of("OBSERVER"))
|
|
95
|
+
print("BITPIX" in h, list(h.keys()))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### WCS (celestial)
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
with zf.open("wcs.fits") as hdul:
|
|
102
|
+
lon, lat = hdul[0].pix2world(40.0, 30.0) # 1-based pixel (FITS CRPIX convention)
|
|
103
|
+
px, py = hdul[0].world2pix(lon, lat)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Low-level (ctypes)
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import ctypes as c
|
|
110
|
+
import zigfitsio.lowlevel as ll
|
|
111
|
+
|
|
112
|
+
h = c.c_void_p()
|
|
113
|
+
ll.check(ll.lib.zf_create_memory(None, c.byref(h)))
|
|
114
|
+
ll.check(ll.lib.zf_create_img(h, -32, 2, (c.c_long * 2)(4, 3)))
|
|
115
|
+
ll.lib.zf_close(h)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Conventions
|
|
119
|
+
|
|
120
|
+
- Image and table data is exchanged as **native-endian** NumPy arrays; non-native (byte-swapped)
|
|
121
|
+
input is coerced automatically before writing. Image shape is the reversed FITS axis order
|
|
122
|
+
(`(NAXIS2, NAXIS1)`), C-contiguous — identical memory layout to `astropy.io.fits`.
|
|
123
|
+
- `BSCALE`/`BZERO` and `TSCAL`/`TZERO` scaling and the unsigned-integer convention are applied
|
|
124
|
+
automatically on read (images and table columns) and honored on write; the output dtype is
|
|
125
|
+
widened to float when real scaling is present, or to `u2/u4/u8` for the unsigned convention.
|
|
126
|
+
- Errors are raised as typed `FitsError` subclasses (`KeywordNotFound` is also a `KeyError`).
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
zig build capi # build the shared library into zig-out/lib
|
|
132
|
+
pip install -e .[test] # editable install (builds the lib via the hook)
|
|
133
|
+
pytest bindings/python/tests -q # run the suite (incl. astropy cross-checks)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
When running tests against an uninstalled checkout, point the loader at the dev build:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
ZIGFITSIO_LIBRARY=$PWD/zig-out/lib/libzigfitsio_capi.dylib \
|
|
140
|
+
PYTHONPATH=bindings/python/src pytest bindings/python/tests -q
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT — see [`LICENSE`](../../LICENSE).
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# zigfitsio
|
|
2
|
+
|
|
3
|
+
A pure-[Zig](https://ziglang.org) implementation of a [FITS](https://fits.gsfc.nasa.gov)
|
|
4
|
+
(Flexible Image Transport System) 4.0 input/output library, with feature parity goals
|
|
5
|
+
against [CFITSIO](https://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html) — written
|
|
6
|
+
with **no C imports and no C sources** (`GC-1`).
|
|
7
|
+
|
|
8
|
+
- **Conformance target:** *Definition of the FITS Standard*, Version 4.0 (2018-08-13).
|
|
9
|
+
- **Toolchain:** Zig **0.16.0**, standard library only (`GC-2`, `GC-3`).
|
|
10
|
+
- **License:** MIT (see [`LICENSE`](./LICENSE)).
|
|
11
|
+
|
|
12
|
+
See [`requirements.md`](./requirements.md), [`design.md`](./design.md), and
|
|
13
|
+
[`tasks.md`](./tasks.md) for the full specification, architecture, and implementation
|
|
14
|
+
backlog.
|
|
15
|
+
|
|
16
|
+
## Build
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
zig build # build the static library
|
|
20
|
+
zig build test # run the test suite
|
|
21
|
+
zig build capi # build the C-ABI shared library (for the Python/C bindings)
|
|
22
|
+
zig build capi-test # test the C-ABI shim
|
|
23
|
+
zig build bench # throughput benchmarks
|
|
24
|
+
zig build fitsverify # run the structural-validation CLI demo
|
|
25
|
+
zig build fuzz # fuzz the header/table parsers
|
|
26
|
+
zig build wasm-check # compile the core for wasm32-freestanding
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Use as a dependency
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
zig fetch --save git+https://github.com/anhydrous99/zigfitsio
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```zig
|
|
36
|
+
// build.zig
|
|
37
|
+
const fits = b.dependency("zigfitsio", .{ .target = target, .optimize = optimize });
|
|
38
|
+
exe.root_module.addImport("zigfitsio", fits.module("zigfitsio"));
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Language bindings (C ABI + Python)
|
|
42
|
+
|
|
43
|
+
A stable **C ABI** and a **Python** package live under [`bindings/`](./bindings). They are
|
|
44
|
+
additive — the pure-Zig library in `src/` is unchanged — and are layered:
|
|
45
|
+
|
|
46
|
+
- **C-ABI shim** ([`bindings/capi/`](./bindings/capi), `zig build capi`): a dynamic library
|
|
47
|
+
`zigfitsio_capi` exporting `zf_*` symbols. The comptime-generic Zig API is monomorphized behind
|
|
48
|
+
runtime datatype codes, and Zig errors are surfaced as CFITSIO-compatible status ints via
|
|
49
|
+
`errors.cfitsioStatus`. The hand-written contract is [`bindings/c/zigfitsio.h`](./bindings/c/zigfitsio.h).
|
|
50
|
+
This is **not** a CFITSIO `fits_*`/`ff*` drop-in — it is a purpose-built ABI for bindings.
|
|
51
|
+
- **Low-level Python** (`zigfitsio.lowlevel`): a 1:1 `ctypes` binding over the C ABI, with a typed
|
|
52
|
+
`FitsError` hierarchy. Pure Python — no C compiler needed at install.
|
|
53
|
+
- **High-level Python** (`zigfitsio`): a NumPy-first API modeled on `astropy.io.fits` — `open`,
|
|
54
|
+
`HDUList`, the HDU classes, `Column`, a dict-like `Header`, `getdata`/`getheader`/`writeto`/
|
|
55
|
+
`verify`, and celestial WCS transforms. Interoperability is verified **both directions** against
|
|
56
|
+
Astropy and the committed CFITSIO golden corpus.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import numpy as np, zigfitsio as zf
|
|
60
|
+
|
|
61
|
+
zf.writeto("img.fits", np.arange(12, dtype="f4").reshape(3, 4), overwrite=True)
|
|
62
|
+
with zf.open("img.fits") as hdul:
|
|
63
|
+
print(hdul[0].data, hdul[0].header["NAXIS1"])
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
See [`bindings/python/README.md`](./bindings/python/README.md) for install, the full API, and the
|
|
67
|
+
packaging/wheel workflow.
|
|
68
|
+
|
|
69
|
+
## Status
|
|
70
|
+
|
|
71
|
+
Feature-complete and tested (459 tests green), with cross-tool interoperability verified against
|
|
72
|
+
**CFITSIO 4.6.4 + Astropy** (see below). Implemented end to end:
|
|
73
|
+
|
|
74
|
+
- **Foundation:** I/O layer (in-memory, file, stream/gzip, and HTTP backends), typed
|
|
75
|
+
error sets, big-endian access, numeric-conversion policy, resource limits.
|
|
76
|
+
- **Headers:** value parsing (null/empty/undefined distinction), 80-byte cards, full
|
|
77
|
+
read + edit operations, header-space pre-allocation, `CONTINUE` long strings,
|
|
78
|
+
`HIERARCH` long names, and complex-valued cards.
|
|
79
|
+
- **HDUs & data:** HDU model with navigation/mutation; `ImageView` over all six `BITPIX`
|
|
80
|
+
with scaling, nulls, and strided sections; ASCII and binary tables (all `TFORM` codes,
|
|
81
|
+
`TDIM`, scaling, nulls); variable-length arrays with a compacting heap.
|
|
82
|
+
- **WCS:** keyword set parse/serialize plus celestial (`TAN`/`SIN`/`ARC`/`STG`/`ZEA`/`CAR`),
|
|
83
|
+
spectral, and time-coordinate transforms.
|
|
84
|
+
- **Compression:** GZIP_1/2, RICE_1, PLIO_1, and HCOMPRESS_1 tiled read **and** write,
|
|
85
|
+
with subtractive dithering; tile-compressed-table (`ZTABLE`) reading.
|
|
86
|
+
- **Integrity & validation:** `DATASUM`/`CHECKSUM` compute/update/verify; a
|
|
87
|
+
`fitsverify`-style structural pass.
|
|
88
|
+
- **Convenience:** CFITSIO-style extended filenames, ASCII header templates, hierarchical
|
|
89
|
+
grouping tables, and a transparent `.fits.gz` open path.
|
|
90
|
+
|
|
91
|
+
**Cross-tool interoperability is verified**, not just self-consistent: a committed
|
|
92
|
+
**CFITSIO 4.6.4 + `fpack`** golden corpus (`test/golden/`, generators under `interop/`) is decoded
|
|
93
|
+
hermetically by `test/golden.zig` on every CI cell (including big-endian s390x), an in-house
|
|
94
|
+
full-feature round-trip (`test/e2e.zig`) mirrors CFITSIO's `testprog.c`, and a dedicated `interop`
|
|
95
|
+
CI job opens every zigfitsio-written file with CFITSIO `funpack`, Astropy, and `fitsverify`.
|
|
96
|
+
Authoring this corpus closed two real interop bugs that self-round-trips could not catch (the PLIO
|
|
97
|
+
line-list header + `COMPRESSED_DATA` `1PB`→`1PI`, and an unregistered `checksum_on_close` hook).
|
|
98
|
+
The one remaining codec limit is `HCOMPRESS_1` lossy smoothing (`hsmooth`) — lossless only. See
|
|
99
|
+
`CAVEATS.md` and `tasks.md`.
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Read an image into `[]f32`
|
|
104
|
+
|
|
105
|
+
```zig
|
|
106
|
+
const fits = @import("zigfitsio");
|
|
107
|
+
|
|
108
|
+
var f = try fits.openFile(allocator, "img.fits", .read_only, .{});
|
|
109
|
+
defer f.deinit();
|
|
110
|
+
|
|
111
|
+
var img = try fits.ImageView.of(&f, f.current());
|
|
112
|
+
const buf = try allocator.alloc(f32, @intCast(img.elementCount()));
|
|
113
|
+
defer allocator.free(buf);
|
|
114
|
+
|
|
115
|
+
// BSCALE/BZERO applied, NaN → 0 substituted, stored → f32 converted, in bounded chunks.
|
|
116
|
+
try img.readAll(f32, buf, .{ .null_sentinel = 0.0 });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Create an image (the programmatic builder is the primary path)
|
|
120
|
+
|
|
121
|
+
```zig
|
|
122
|
+
var f = try fits.createFile(allocator, "out.fits", .{});
|
|
123
|
+
defer f.deinit();
|
|
124
|
+
|
|
125
|
+
var img = try fits.ImageView.append(&f, .{ .bitpix = -32, .axes = &.{ 256, 256 } });
|
|
126
|
+
try img.writeAll(f32, pixels, .{});
|
|
127
|
+
try f.flush();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Navigate HDUs and read a strided section
|
|
131
|
+
|
|
132
|
+
```zig
|
|
133
|
+
const n = try f.hduCount();
|
|
134
|
+
var img = try fits.ImageView.of(&f, try f.select(2)); // 1-based HDU number
|
|
135
|
+
var tile: [256 * 256]f32 = undefined;
|
|
136
|
+
try img.readSection(f32, &.{ 0, 0 }, &.{ 511, 511 }, &.{ 2, 2 }, &tile, .{});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
In-memory and stdin/stdout back-ends are first-class: build a `fits.MemoryDevice` and pass
|
|
140
|
+
`mem.device()` to `fits.open`/`fits.create` for a freestanding-capable, file-less path.
|