Skip to content

Commit dd1c65c

Browse files
committed
Handles python versioning
1 parent c058c42 commit dd1c65c

File tree

6 files changed

+49
-15
lines changed

6 files changed

+49
-15
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Unreleased
77
writing multiple cutouts into a single ZIP archive. [#167]
88
- Added ``get_tess_sectors`` function to return TESS sector information for sectors whose footprints overlap with
99
the given sky coordinates and cutout size. [#168]
10+
- Cutouts of ASDF data in FITS format now include embedded ASDF metadata and cutout data in an "ASDF" extension within the FITS file for
11+
Python versions greater than or equal to 3.11. [#170]
1012

1113
Breaking Changes
1214
^^^^^^^^^^^^^^^^

astrocut/asdf_cutout.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import warnings
23
from copy import deepcopy
34
from pathlib import Path
@@ -17,11 +18,10 @@
1718
from astropy.utils.decorators import deprecated_renamed_argument
1819
from astropy.wcs import WCS
1920
from s3path import S3Path
20-
from stdatamodels import asdf_in_fits
2121

2222
from . import log, __version__
2323
from .image_cutout import ImageCutout
24-
from .exceptions import DataWarning, InvalidQueryError, InvalidInputError
24+
from .exceptions import DataWarning, ModuleWarning, InvalidQueryError, InvalidInputError
2525

2626

2727
class ASDFCutout(ImageCutout):
@@ -84,6 +84,9 @@ def __init__(self, input_files: List[Union[str, Path, S3Path]], coordinates: Uni
8484
# Superclass constructor
8585
super().__init__(input_files, coordinates, cutout_size, fill_value, verbose=verbose)
8686

87+
# Must be using Python 3.11 or higher to support stdatamodels and ASDF-in-FITS embedding
88+
self._supports_stdatamodels = sys.version_info >= (3, 11)
89+
8790
# Assign AWS credential attributes
8891
self._key = key
8992
self._secret = secret
@@ -106,6 +109,18 @@ def fits_cutouts(self) -> List[fits.HDUList]:
106109
Return the cutouts as a list `astropy.io.fits.HDUList` objects.
107110
"""
108111
if not self._fits_cutouts:
112+
# Try to import stdatamodels for ASDF-in-FITS embedding
113+
if self._supports_stdatamodels:
114+
try:
115+
from stdatamodels import asdf_in_fits
116+
except ModuleNotFoundError:
117+
warnings.warn('The `stdatamodels` package is required for ASDF-in-FITS embedding. Skipping embedding for these cutouts. '
118+
'To install astrocut with optional `stdatamodels` support, run: pip install "astrocut[all]"', ModuleWarning)
119+
self._supports_stdatamodels = False
120+
else:
121+
warnings.warn('ASDF-in-FITS embedding requires Python 3.11 or higher. '
122+
'Skipping embedding for these cutouts.')
123+
109124
fits_cutouts = []
110125
for i, (file, cutouts) in enumerate(self.cutouts_by_file.items()):
111126
cutout = cutouts[0]
@@ -119,8 +134,10 @@ def fits_cutouts(self) -> List[fits.HDUList]:
119134
primary_hdu.header['ORIG_FLE'] = file # Add original file to header
120135
hdul = fits.HDUList([primary_hdu])
121136

122-
# Embed ASDF into FITS
123-
hdul_embed = asdf_in_fits.to_hdulist(tree, hdul)
137+
if self._supports_stdatamodels:
138+
hdul_embed = asdf_in_fits.to_hdulist(tree, hdul)
139+
else:
140+
hdul_embed = hdul
124141
fits_cutouts.append(hdul_embed)
125142
self._fits_cutouts = fits_cutouts
126143
return self._fits_cutouts

astrocut/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,10 @@ class DataWarning(AstropyWarning):
5050
Warnings to do with data content.
5151
"""
5252
pass
53+
54+
55+
class ModuleWarning(AstropyWarning):
56+
"""
57+
Warnings to do with optional modules.
58+
"""
59+
pass

astrocut/tests/test_asdf_cutout.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44
import zipfile
55
import io
6+
import importlib.util
67

78
import asdf
89
from astropy import coordinates as coord
@@ -14,7 +15,6 @@
1415
from astropy.io import fits
1516
from gwcs import wcs, coordinate_frames
1617
from PIL import Image
17-
from stdatamodels import asdf_in_fits
1818

1919
from astrocut.asdf_cutout import ASDFCutout, asdf_cut, get_center_pixel
2020
from astrocut.exceptions import DataWarning, InvalidInputError, InvalidQueryError
@@ -196,9 +196,11 @@ def check_asdf_metadata(af, original_file, cutout_data):
196196
assert hdul[0].header['ORIG_FLE'] == test_images[i].as_posix()
197197
assert Path(fits_file).stat().st_size < Path(test_images[i]).stat().st_size
198198

199-
# Check ASDF extension contents
200-
with asdf_in_fits.open(fits_file) as af:
201-
check_asdf_metadata(af, test_images[i], cutout.cutouts[i].data)
199+
# Check ASDF extension contents (stdatamodels optional)
200+
if importlib.util.find_spec('stdatamodels') is not None:
201+
from stdatamodels import asdf_in_fits
202+
with asdf_in_fits.open(fits_file) as af:
203+
check_asdf_metadata(af, test_images[i], cutout.cutouts[i].data)
202204

203205

204206
@pytest.mark.parametrize('output_format', ['.asdf', '.fits'])
@@ -224,7 +226,7 @@ def test_asdf_cutout_write_to_zip(tmpdir, test_images, center_coord, cutout_size
224226
else:
225227
with fits.open(io.BytesIO(data)) as hdul:
226228
assert isinstance(hdul, fits.HDUList)
227-
assert len(hdul) == 2 # primary + embedded ASDF extension
229+
assert len(hdul) == 2 if importlib.util.find_spec('stdatamodels') is not None else 1
228230
assert hdul[0].data.shape == (cutout_size, cutout_size)
229231

230232

@@ -254,13 +256,16 @@ def check_lite_metadata(af):
254256
# Write cutouts to HDUList objects in lite mode
255257
cutout = ASDFCutout(test_images, center_coord, cutout_size, lite=True)
256258
for hdul in cutout.fits_cutouts:
257-
assert len(hdul) == 2 # primary HDU + embedded ASDF extension
259+
has_stdatamodels = importlib.util.find_spec('stdatamodels') is not None
260+
assert len(hdul) == 2 if has_stdatamodels else 1 # primary HDU + embedded ASDF extension
258261
assert hdul[0].name == 'PRIMARY'
259-
assert hdul[1].name == 'ASDF'
260262

261-
# Check ASDF extension contents
262-
with asdf_in_fits.open(hdul) as af:
263-
check_lite_metadata(af)
263+
# Check ASDF extension contents (stdatamodels optional)
264+
if has_stdatamodels:
265+
assert hdul[1].name == 'ASDF'
266+
from stdatamodels import asdf_in_fits
267+
with asdf_in_fits.open(hdul) as af:
268+
check_lite_metadata(af)
264269

265270

266271
def test_asdf_cutout_partial(test_images, center_coord, cutout_size):

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ install_requires =
2525
requests>=2.32.3 # for making HTTP requests
2626
spherical_geometry>=1.3.0
2727
gwcs>=0.21.0
28-
stdatamodels>=4.1.0
2928
scipy
3029
Pillow
3130

@@ -42,6 +41,8 @@ docs =
4241
docutils == 0.16
4342
sphinx-astropy
4443
sphinx_rtd_theme >= 0.5.2
44+
all =
45+
stdatamodels>=4.1.0 # stdatamodels is optional; required for ASDF-in-FITS embedding (Python>=3.11)
4546

4647
[options.package_data]
4748
astrocut.tests = data/*

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ deps =
5151

5252
astroquery04: astroquery==0.4.*
5353

54+
stdatamodels; python_version >= "3.11"
55+
5456
devdeps: git+https://github.com/numpy/numpy.git#egg=numpy
5557
devdeps: git+https://github.com/astropy/astropy.git#egg=astropy
5658
devdeps: git+https://github.com/astropy/astroquery.git

0 commit comments

Comments
 (0)