diff --git a/docs/source/jwst/datamodels/models.rst b/docs/source/jwst/datamodels/models.rst index 9e6c2b19..126fe410 100644 --- a/docs/source/jwst/datamodels/models.rst +++ b/docs/source/jwst/datamodels/models.rst @@ -293,25 +293,29 @@ use:: Extra FITS keywords ------------------- +.. warning:: + + This feature is deprecated and will be removed in a future release. + When loading arbitrary FITS files, there may be keywords that are not listed in the schema for that data model. These "extra" FITS keywords -are put under the model in the `_extra_fits` namespace. +are put under the model in the `extra_fits` namespace. -Under the `_extra_fits` namespace is a section for each header data +Under the `extra_fits` namespace is a section for each header data unit, and under those are the extra FITS keywords. For example, if the FITS file contains a keyword `FOO` in the primary header, its value can be obtained using:: - model._extra_fits.PRIMARY.FOO + model.extra_fits.PRIMARY.FOO This feature is useful to retain any extra keywords from input files to output products. -To get a list of everything in `_extra_fits`:: +To get a list of everything in `extra_fits`:: - model._extra_fits._instance + model.extra_fits._instance -returns a dictionary of of the instance at the model._extra_fits node. +returns a dictionary of of the instance at the model.extra_fits node. `_instance` can be used at any node in the tree to return a dictionary of rest of the tree structure at that node. diff --git a/docs/source/jwst/datamodels/structure.rst b/docs/source/jwst/datamodels/structure.rst index 57c7087a..abd3dfce 100644 --- a/docs/source/jwst/datamodels/structure.rst +++ b/docs/source/jwst/datamodels/structure.rst @@ -68,6 +68,12 @@ not found in the schema are placed in the header subtree and data is placed in the data subtree. Finally, it reads the history keywords and places them in a history structure. +.. note:: + + Manipulation of `extra_fits` is a deprecated feature. The way + stdatamodels handles FITS keywords that are not in the schema is + likely to change in a future release. + To write a model back to a file, call the save method on the file. It first calls validate_required to check the schema to see if all the required fields are present in the model. Then it calls the function diff --git a/src/stdatamodels/jwst/datamodels/tests/test_models.py b/src/stdatamodels/jwst/datamodels/tests/test_models.py index cd3fefde..1117cc14 100644 --- a/src/stdatamodels/jwst/datamodels/tests/test_models.py +++ b/src/stdatamodels/jwst/datamodels/tests/test_models.py @@ -202,8 +202,11 @@ def test_update_from_datamodel(tmp_path, datamodel_for_update, only, extra_fits) # Verify the fixture returns keywords we expect assert oldim.meta.telescope == "JWST" assert oldim.meta.wcsinfo.crval1 == 5 - assert oldim.extra_fits.PRIMARY.header == [["FOO", "BAR", ""]] - assert oldim.extra_fits.SCI.header == [["BAZ", "BUZ", ""]] + with pytest.raises( + DeprecationWarning, match="Manipulation of extra_fits is deprecated" + ): + assert oldim.extra_fits.PRIMARY.header == [["FOO", "BAR", ""]] + assert oldim.extra_fits.SCI.header == [["BAZ", "BUZ", ""]] newim.update(oldim, only=only, extra_fits=extra_fits) newim.save(path) diff --git a/src/stdatamodels/model_base.py b/src/stdatamodels/model_base.py index f71f9b22..a6a5b148 100644 --- a/src/stdatamodels/model_base.py +++ b/src/stdatamodels/model_base.py @@ -682,7 +682,24 @@ def shape(self): self._shape = primary_array.shape return self._shape + def __getattribute__(self, attr): + if attr in ("extra_fits", "_extra_fits"): + warnings.warn( + "Manipulation of extra_fits is deprecated. " + "This feature will be removed in an upcoming release", + DeprecationWarning, + stacklevel=2, + ) + return object.__getattribute__(self, attr) + def __setattr__(self, attr, value): + if attr in ("extra_fits", "_extra_fits"): + warnings.warn( + "Manipulation of extra_fits is deprecated. " + "This feature will be removed in an upcoming release", + DeprecationWarning, + stacklevel=2, + ) if attr in frozenset(("shape", "history", "_extra_fits", "schema")): object.__setattr__(self, attr, value) else: diff --git a/tests/test_fits.py b/tests/test_fits.py index e53fc216..c9429e6a 100644 --- a/tests/test_fits.py +++ b/tests/test_fits.py @@ -93,7 +93,8 @@ def test_extra_fits(tmp_path): hdul.writeto(file_path, overwrite=True) with DataModel(file_path) as dm: - assert any(h for h in dm.extra_fits.PRIMARY.header if h == ["FOO", "BAR", ""]) + with pytest.raises(DeprecationWarning, match="Manipulation of extra_fits is deprecated"): + assert any(h for h in dm.extra_fits.PRIMARY.header if h == ["FOO", "BAR", ""]) def test_hdu_order(tmp_path): @@ -600,11 +601,12 @@ def test_no_asdf_extension_extra_fits(tmp_path): } with PureFitsModel((5, 5)) as m: - m.extra_fits = {} - m.extra_fits.instance.update(extra_fits) - assert "ASDF" in m.extra_fits.instance - assert "CHECKSUM" in m.extra_fits.ASDF.header[0] - assert "DATASUM" in m.extra_fits.ASDF.header[1] + with pytest.raises(DeprecationWarning, match="Manipulation of extra_fits is deprecated"): + m.extra_fits = {} + m.extra_fits.instance.update(extra_fits) + assert "ASDF" in m.extra_fits.instance + assert "CHECKSUM" in m.extra_fits.ASDF.header[0] + assert "DATASUM" in m.extra_fits.ASDF.header[1] m.save(path) with fits.open(path, memmap=False) as hdulist: @@ -612,7 +614,10 @@ def test_no_asdf_extension_extra_fits(tmp_path): with PureFitsModel(path) as m: with pytest.raises(AttributeError): - m.extra_fits # noqa: B018 + with pytest.raises( + DeprecationWarning, match="Manipulation of extra_fits is deprecated" + ): + m.extra_fits # noqa: B018 def test_ndarray_validation(tmp_path): diff --git a/tests/test_models.py b/tests/test_models.py index 81205ce8..7fb28ef5 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -341,3 +341,15 @@ def find_gen_by_id(object_id): # many models which would indicate they are difficult to garbage # collect. assert len(mids) < 2 + + +def test_extra_fits_deprecation(): + m = DataModel() + with pytest.warns(DeprecationWarning): + m.extra_fits = "foo" + with pytest.warns(DeprecationWarning): + m._extra_fits = "bar" + with pytest.warns(DeprecationWarning): + _ = m.extra_fits + with pytest.warns(DeprecationWarning): + _ = m._extra_fits