Skip to content

Commit

Permalink
remove deprecated skip_fits_update
Browse files Browse the repository at this point in the history
  • Loading branch information
braingram committed Jan 23, 2025
1 parent b49f5f4 commit f4b2a5d
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 228 deletions.
95 changes: 5 additions & 90 deletions src/stdatamodels/fits_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,18 +573,10 @@ def _schema_has_fits_hdu(schema):
return has_fits_hdu[0]


def _load_from_schema(hdulist, schema, tree, context, skip_fits_update=False):
def _load_from_schema(hdulist, schema, tree, context):
known_keywords = {}
known_datas = set()

# Check if there are any table HDU's. If not, this whole process
# can be skipped.
if skip_fits_update:
if not any(isinstance(hdu, fits.BinTableHDU) for hdu in hdulist if hdu.name != 'ASDF'):
log.debug('Skipping FITS updating completely.')
return known_keywords, known_datas
log.debug('Skipping FITS keyword updating except for BinTableHDU and its associated header keywords.')

# Determine maximum EXTVER that could be used in finding named HDU's.
# This is needed to constrain the loop over HDU's when resolving arrays.
max_extver = max(hdu.ver for hdu in hdulist) if len(hdulist) else 0
Expand All @@ -597,7 +589,7 @@ def _load_from_schema(hdulist, schema, tree, context, skip_fits_update=False):

def callback(schema, path, combiner, ctx, recurse):
result = None
if not skip_fits_update and 'fits_keyword' in schema:
if 'fits_keyword' in schema:
fits_keyword = schema['fits_keyword']
result = _fits_keyword_loader(
hdulist, fits_keyword, schema,
Expand Down Expand Up @@ -683,7 +675,7 @@ def _load_history(hdulist, tree):
history['entries'].append(HistoryEntry({'description': entry}))


def from_fits(hdulist, schema, context, skip_fits_update=None, **kwargs):
def from_fits(hdulist, schema, context, **kwargs):
"""Read model information from a FITS HDU list
Parameters
Expand All @@ -696,32 +688,16 @@ def from_fits(hdulist, schema, context, skip_fits_update=None, **kwargs):
context: DataModel
The `DataModel` to update
skip_fits_update : bool or None
DEPRECATED
When `False`, models opened from FITS files will proceed
and load the FITS header values into the model.
When `True` and the FITS file has an ASDF extension, the
loading/validation of the FITS header will be skipped, loading
the model only from the ASDF extension.
When `None`, the value is taken from the environmental SKIP_FITS_UPDATE.
Otherwise, the default is `False`
"""
try:
ff = from_fits_asdf(hdulist, **kwargs)
except Exception as exc:
raise exc.__class__("ERROR loading embedded ASDF: " + str(exc)) from exc

# Determine whether skipping the FITS loading can be done.
skip_fits_update = _verify_skip_fits_update(
skip_fits_update, hdulist, ff, context
)

known_keywords, known_datas = _load_from_schema(
hdulist, schema, ff.tree, context, skip_fits_update=skip_fits_update
hdulist, schema, ff.tree, context
)
if not skip_fits_update:
_load_extra_fits(hdulist, known_keywords, known_datas, ff.tree)
_load_extra_fits(hdulist, known_keywords, known_datas, ff.tree)

_load_history(hdulist, ff.tree)

Expand Down Expand Up @@ -810,67 +786,6 @@ def from_fits_hdu(hdu, schema):
return data


def _verify_skip_fits_update(skip_fits_update, hdulist, asdf_struct, context):
"""Ensure all conditions for skipping FITS updating are true
Returns True if either 1) the FITS hash in the asdf structure matches the input
FITS structure. Or 2) skipping has been explicitly asked for in `skip_fits_update`.
Parameters
----------
skip_fits_update : bool
Regardless of FIT hash check, attempt to skip if requested.
hdulist : astropy.io.fits.HDUList
The input FITS information
asdf_struct : asdf.ASDFFile
The associated ASDF structure
context : DataModel
The DataModel being built.
Returns
-------
skip_fits_update : bool
All conditions are satisfied for skipping FITS updating.
"""
if skip_fits_update is None:
skip_fits_update = util.get_envar_as_boolean('SKIP_FITS_UPDATE', None)
if skip_fits_update is not None:
# warn if the value was not None (defined by the user)
warnings.warn("skip_fits_update is deprecated and will be removed", DeprecationWarning)

# If skipping has been explicitly disallowed, indicate as such.
if skip_fits_update is False:
return False

# Skipping has either been requested or has been left to be determined automatically.
# Continue checking conditions necessary for skipping.

# Need an already existing ASDF. If not, cannot skip.
if not len(asdf_struct.tree):
log.debug('No ASDF information found. Cannot skip updating from FITS headers.')
return False

# Ensure model types match
hdulist_model_type = util.get_model_type(hdulist)
if hdulist_model_type != context.__class__.__name__:
log.debug(f'Input model type {hdulist_model_type} does not match the'
f' requested model {type(context)}.'
' Cannot skip updating from FITS headers.')
return False

# Check for FITS hash and compare to current. If equal, automatically skip.
if asdf_struct.tree.get(FITS_HASH_KEY, None) is not None:
if asdf_struct.tree[FITS_HASH_KEY] == fits_hash(hdulist):
log.debug('FITS hash matches. Skipping FITS updating.')
return True

# If skip only if explicitly requested.
return False if skip_fits_update is None else True


def fits_hash(hdulist):
"""Calculate a hash based on all HDU headers
Expand Down
71 changes: 0 additions & 71 deletions src/stdatamodels/jwst/datamodels/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,6 @@
ASN_FILE = os.path.join(ROOT_DIR, 'association.json')


@pytest.fixture
def make_models(tmp_path):
"""Create basic models
Returns
-------
path_just_fits, path_model : (str, str)
`path_just_fits` is a FITS file of `JwstDataModel` without the ASDF extension.
`path_model` is a FITS file of `JwstDataModel` with the ASDF extension.
"""
path_just_fits = tmp_path / 'just_fits.fits'
path_model = tmp_path / 'model.fits'
primary_hdu = fits.PrimaryHDU()
primary_hdu.header['EXP_TYPE'] = 'NRC_IMAGE'
primary_hdu.header['DATAMODL'] = "JwstDataModel"
hduls = fits.HDUList([primary_hdu])
hduls.writeto(path_just_fits)
model = JwstDataModel(hduls)
model.save(path_model)
return {
'just_fits': path_just_fits,
'model': path_model
}


def test_init_from_pathlib(tmp_path):
"""Test initializing model from a Path object"""
path = tmp_path / "pathlib.fits"
Expand All @@ -62,52 +37,6 @@ def test_init_from_pathlib(tmp_path):
assert isinstance(model, ImageModel)


@pytest.mark.parametrize('which_file, skip_fits_update, expected_exp_type',
[
('just_fits', None, 'FGS_DARK'),
('just_fits', False, 'FGS_DARK'),
('just_fits', True, 'FGS_DARK'),
('model', None, 'FGS_DARK'),
('model', False, 'FGS_DARK'),
('model', True, 'NRC_IMAGE')
]
)
@pytest.mark.parametrize('use_env', [False, True])
def test_skip_fits_update(jail_environ,
use_env,
make_models,
which_file,
skip_fits_update,
expected_exp_type):
"""Test skip_fits_update setting"""
# Setup the FITS file, modifying a header value
path = make_models[which_file]
with fits.open(path) as hduls:
hduls[0].header['exp_type'] = 'FGS_DARK'

# Decide how to skip. If using the environmental,
# set that and pass None to the open function.
try:
del os.environ['SKIP_FITS_UPDATE']
except KeyError:
# No need to worry, environmental doesn't exist anyways
pass

if skip_fits_update is not None:
ctx = pytest.warns(DeprecationWarning, match="skip_fits_update is deprecated")
else:
ctx = contextlib.nullcontext()

if use_env:
if skip_fits_update is not None:
os.environ['SKIP_FITS_UPDATE'] = str(skip_fits_update)
skip_fits_update = None

with ctx:
with datamodels.open(hduls, skip_fits_update=skip_fits_update) as model:
assert model.meta.exposure.type == expected_exp_type


def test_asnmodel_table_size_zero():
with AsnModel() as dm:
assert len(dm.asn_table) == 0
Expand Down
2 changes: 1 addition & 1 deletion src/stdatamodels/jwst/datamodels/tests/test_multislit.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_multislit_copy(tmp_path):
path = tmp_path / "multislit.fits"
with MultiSlitModel() as input:
for i in range(4):
input.slits.append(input.slits.item(data=np.empty((50, 50))))
input.slits.append(input.slits.item(data=np.empty((50, 50), dtype='f4')))

assert len(input.slits) == 4
input.save(path)
Expand Down
8 changes: 0 additions & 8 deletions src/stdatamodels/jwst/datamodels/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ def open(init=None, guess=True, memmap=False, **kwargs):
If `True`, arrays will be validated against ndim, max_ndim, and datatype
validators in the schemas.
- FITS
skip_fits_update : bool or None
DEPRECATED
`True` to skip updating the ASDF tree from the FITS headers, if possible.
If `None`, value will be taken from the environmental SKIP_FITS_UPDATE.
Otherwise, the default value is `True`.
Returns
-------
model : DataModel instance
Expand Down
10 changes: 1 addition & 9 deletions src/stdatamodels/model_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,7 @@ def __init__(self, init=None, schema=None, memmap=False,
kwargs : dict
Additional keyword arguments passed to lower level functions. These arguments
are generally file format-specific. Arguments of note are:
- FITS
skip_fits_update - bool or None
DEPRECATED
`True` to skip updating the ASDF tree from the FITS headers, if possible.
If `None`, value will be taken from the environmental SKIP_FITS_UPDATE.
Otherwise, the default value is `True`.
are generally file format-specific.
"""

# Override value of validation parameters if not explicitly set.
Expand Down
64 changes: 15 additions & 49 deletions tests/test_fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from stdatamodels import DataModel
from stdatamodels import fits_support
from stdatamodels.jwst import datamodels

from models import FitsModel, PureFitsModel

Expand Down Expand Up @@ -381,58 +382,23 @@ def test_ensure_ascii():
fits_support.ensure_ascii(inp) == "ABCDEFG"


@pytest.mark.parametrize(
'which_file, skip_fits_update, expected_exp_type',
[
('just_fits', None, 'FGS_DARK'),
('just_fits', False, 'FGS_DARK'),
('just_fits', True, 'FGS_DARK'),
('model', None, 'FGS_DARK'),
('model', False, 'FGS_DARK'),
('model', True, 'NRC_IMAGE')
]
)
@pytest.mark.parametrize(
'use_env',
[False, True]
)
def test_skip_fits_update(tmp_path,
monkeypatch,
use_env,
which_file,
skip_fits_update,
expected_exp_type):
"""Test skip_fits_update setting"""
def test_fits_update(tmp_path):
"""Test that FITS changes are seen when datamodels are opened"""
file_path = tmp_path/"test.fits"
expected_exp_type = 'FGS_DARK'

# Setup the FITS file, modifying a header value
if which_file == "just_fits":
primary_hdu = fits.PrimaryHDU()
primary_hdu.header['EXP_TYPE'] = 'NRC_IMAGE'
primary_hdu.header['DATAMODL'] = "FitsModel"
hduls = fits.HDUList([primary_hdu])
hduls.writeto(file_path)
else:
model = FitsModel()
model.meta.exposure.type = 'NRC_IMAGE'
model.save(file_path)

with fits.open(file_path) as hduls:
hduls[0].header['EXP_TYPE'] = 'FGS_DARK'

if skip_fits_update is not None:
ctx = pytest.warns(DeprecationWarning, match="skip_fits_update is deprecated")
else:
ctx = contextlib.nullcontext()

if use_env:
if skip_fits_update is not None:
monkeypatch.setenv("SKIP_FITS_UPDATE", str(skip_fits_update))
skip_fits_update = None

with ctx:
model = FitsModel(hduls, skip_fits_update=skip_fits_update)
assert model.meta.exposure.type == expected_exp_type
model = datamodels.ImageModel()
model.meta.exposure.type = 'NRC_IMAGE'
model.save(file_path)
del model

with fits.open(file_path) as hdulist:
hdulist['PRIMARY'].header['EXP_TYPE'] = expected_exp_type
hdulist.writeto(file_path, overwrite=True)

with datamodels.open(file_path) as model:
assert model.meta.exposure.type == expected_exp_type


def test_from_hdulist(tmp_path):
Expand Down

0 comments on commit f4b2a5d

Please sign in to comment.