Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-27350: Add ability to require mandatory translations and also a subset #42

Merged
merged 1 commit into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 41 additions & 6 deletions python/astro_metadata_translator/observationInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,29 @@ class ObservationInfo:
and a warning will be issued.
search_path : iterable, optional
Override search paths to use during header fix up.
required : `set`, optional
This parameter can be used to confirm that all properties contained
in the set must translate correctly and also be non-None. For the case
where ``pedantic`` is `True` this will still check that the resulting
value is not `None`.
subset : `set`, optional
If not `None`, controls the translations that will be performed
during construction. This can be useful if the caller is only
interested in a subset of the properties and knows that some of
the others might be slow to compute (for example the airmass if it
has to be derived).

Raises
------
ValueError
Raised if the supplied header was not recognized by any of the
registered translators.
registered translators. Also raised if the request property subset
is not a subset of the known properties.
TypeError
Raised if the supplied translator class was not a MetadataTranslator.
KeyError
Raised if a translation fails and pedantic mode is enabled.
Raised if a required property cannot be calculated, or if pedantic
mode is enabled and any translations fails.
NotImplementedError
Raised if the selected translator does not support a required
property.
Expand All @@ -75,7 +88,7 @@ class ObservationInfo:
documentation."""

def __init__(self, header, filename=None, translator_class=None, pedantic=False,
search_path=None):
search_path=None, required=None, subset=None):

# Initialize the empty object
self._header = {}
Expand Down Expand Up @@ -113,8 +126,27 @@ def __init__(self, header, filename=None, translator_class=None, pedantic=False,
else:
file_info = ""

# Determine the properties of interest
all_properties = set(self._PROPERTIES)
if subset is not None:
if not subset:
raise ValueError("Cannot request no properties be calculated.")
if not subset.issubset(all_properties):
raise ValueError("Requested subset is not a subset of known properties. "
f"Got extra: {subset - all_properties}")
properties = subset
else:
properties = all_properties

if required is None:
required = set()
else:
if not required.issubset(all_properties):
raise ValueError("Requested required properties include unknowns: "
f"{required - all_properties}")

# Loop over each property and request the translated form
for t in self._PROPERTIES:
for t in properties:
# prototype code
method = f"to_{t}"
property = f"_{t}"
Expand All @@ -127,7 +159,7 @@ def __init__(self, header, filename=None, translator_class=None, pedantic=False,
except KeyError as e:
err_msg = f"Error calculating property '{t}' using translator {translator.__class__}" \
f"{file_info}"
if pedantic:
if pedantic or t in required:
raise KeyError(err_msg) from e
else:
log.debug("Calculation of property '%s' failed with header: %s", t, header)
Expand All @@ -138,12 +170,15 @@ def __init__(self, header, filename=None, translator_class=None, pedantic=False,
err_msg = f"Value calculated for property '{t}' is wrong type " \
f"({type(value)} != {self._PROPERTIES[t][1]}) using translator {translator.__class__}" \
f"{file_info}"
if pedantic:
if pedantic or t in required:
raise TypeError(err_msg)
else:
log.debug("Calcuation of property '%s' had unexpected type with header: %s", t, header)
log.warning(f"Ignoring {err_msg}")

if value is None and t in required:
raise KeyError(f"Calculation of required property {t} resulted in a value of None")

setattr(self, property, value)

@classmethod
Expand Down
24 changes: 24 additions & 0 deletions tests/test_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ def test_translator(self):
summary = str(v1)
self.assertIn("datetime_begin", summary)

# Create with a subset of properties
v2 = ObservationInfo(header, translator_class=InstrumentTestTranslator,
subset={"telescope", "datetime_begin", "exposure_group"})

self.assertEqual(v2.telescope, v1.telescope)
self.assertEqual(v2.datetime_begin, v2.datetime_begin)
self.assertIsNone(v2.datetime_end)
self.assertIsNone(v2.location)
self.assertIsNone(v2.observation_id)

def test_corrections(self):
"""Apply corrections before translation."""
header = self.header
Expand Down Expand Up @@ -163,6 +173,14 @@ def test_failures(self):
with self.assertRaises(TypeError):
ObservationInfo(header, translator_class=ObservationInfo)

with self.assertRaises(ValueError):
ObservationInfo(header, translator_class=InstrumentTestTranslator,
subset={"definitely_not_known"})

with self.assertRaises(ValueError):
ObservationInfo(header, translator_class=InstrumentTestTranslator,
required={"definitely_not_known"})

with self.assertLogs("astro_metadata_translator"):
with self.assertWarns(UserWarning):
ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False)
Expand All @@ -185,6 +203,12 @@ def test_failures(self):
with self.assertLogs("astro_metadata_translator", level="WARN"):
ObservationInfo(header, translator_class=MissingMethodsTranslator)

with self.assertRaises(KeyError):
with self.assertWarns(UserWarning):
with self.assertLogs("astro_metadata_translator", level="WARN"):
ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False,
required={"boresight_airmass"})


if __name__ == "__main__":
unittest.main()