From c689b9ede6a5544592b666f021e94a30b2137325 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Tue, 19 Nov 2024 16:34:17 +0100 Subject: [PATCH 1/2] Add support for package and mode status keywords --- scopesim/commands/user_commands.py | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/scopesim/commands/user_commands.py b/scopesim/commands/user_commands.py index aaee5fbd..904f1d18 100644 --- a/scopesim/commands/user_commands.py +++ b/scopesim/commands/user_commands.py @@ -169,6 +169,7 @@ def __init__(self, *maps, **kwargs): self._kwargs = deepcopy(kwargs) self.ignore_effects = [] self.package_name = "" + self.package_status = "" self.default_yamls = [] self.modes_dict = {} @@ -201,6 +202,25 @@ def _load_yaml_dict(self, yaml_dict): if "mode_yamls" in yaml_dict: logger.debug(" found mode_yamls") self.update(mode_yamls=yaml_dict["mode_yamls"]) + + if yaml_dict.get("object", "") == "configuration": + # we're in default.yaml + self.package_status = yaml_dict.get("status", "unknown") + if self.package_status == "deprecated": + warn("The selected instrument package is deprecated!", + DeprecationWarning, stacklevel=7) + if self.package_status == "concept": + raise NotImplementedError( + "The selected instrument package is not yet supported." + ) + if self.package_status == "experimental": + # or rather warning? + logger.info( + "The selected instrument package is still in experimental " + "stage, results may not be representative of physical " + "instrument, use with care." + ) + logger.debug(" dict yaml done") def _load_yamls(self, yamls: Collection) -> None: @@ -351,8 +371,23 @@ def set_modes(self, *modes) -> None: raise ValueError(f"mode '{mode}' was not recognised") defyam["properties"]["modes"].append(mode) - if depmsg := self.modes_dict[mode].get("deprecate"): - warn(depmsg, DeprecationWarning, stacklevel=2) + if ((msg := self.modes_dict[mode].get("deprecate", "")) or + self.modes_dict[mode].get("status") == "deprecated"): + # Fallback if status: deprecated but not deprecate key + msg = msg or f"Instrument mode '{mode}' is deprecated." + warn(msg, DeprecationWarning, stacklevel=2) + + if self.modes_dict[mode].get("status") == "experimental": + # or rather warning? + logger.info( + "Mode '%s' is still in experimental stage, results " + "may not be representative of physical instrument." + ) + + if self.modes_dict[mode].get("status") == "concept": + raise NotImplementedError( + f"Instrument mode '{mode}' is not yet supported." + ) # Note: This used to completely reset the instance via the line below. # Calling init like this is bad design, so I replaced is with a @@ -371,8 +406,15 @@ def list_modes(self) -> Iterable[tuple[str, ...]]: cases, it now returns a generator of tuples of strings. """ for mode, subdict in self.modes_dict.items(): - desc = (subdict.get("description", "") + - ":DEPRECATED" * ("deprecate" in subdict)) + # TODO: maybe find a prettier way to print the status... + # TODO: maybe also print mode type (e.g. MICADO SCAO) + desc = ( + subdict.get("description", "") + + f":status={subdict.get('status')}" * ("status" in subdict) + + ":DEPRECATED" * ( + "deprecate" in subdict and "status" not in subdict + ) + ) yield mode, *(s.strip() for s in desc.split(":")) @property From 419289120338abd5d3db72d822ba2a560b78a0c9 Mon Sep 17 00:00:00 2001 From: teutoburg Date: Wed, 20 Nov 2024 12:20:25 +0100 Subject: [PATCH 2/2] Add tests and fix forgotten log arg Turns out, codecov is useful after all... --- scopesim/commands/user_commands.py | 3 +- .../tests/mocks/basic_instrument/default.yaml | 23 +++++++- .../test_basic_instrument.py | 54 ++++++++++++++++++- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/scopesim/commands/user_commands.py b/scopesim/commands/user_commands.py index 904f1d18..a5a476a1 100644 --- a/scopesim/commands/user_commands.py +++ b/scopesim/commands/user_commands.py @@ -381,7 +381,8 @@ def set_modes(self, *modes) -> None: # or rather warning? logger.info( "Mode '%s' is still in experimental stage, results " - "may not be representative of physical instrument." + "may not be representative of physical instrument.", + mode ) if self.modes_dict[mode].get("status") == "concept": diff --git a/scopesim/tests/mocks/basic_instrument/default.yaml b/scopesim/tests/mocks/basic_instrument/default.yaml index fae338b5..1f270daa 100644 --- a/scopesim/tests/mocks/basic_instrument/default.yaml +++ b/scopesim/tests/mocks/basic_instrument/default.yaml @@ -1,8 +1,9 @@ # Default config file for setting up the instrument -object : observation +object: configuration alias : OBS name : test_instrument description : default parameters needed for a basic simulation +status: development packages : - basic_instrument # name of package dependencies @@ -24,6 +25,7 @@ properties : mode_yamls : - name : imaging description: Basic NIR imager + status: development alias : OBS properties : include_slit : False @@ -32,6 +34,7 @@ mode_yamls : - name : spectroscopy description: Basic three-trace long-slit spectrograph + status: development alias: OBS properties : include_slit : True @@ -42,6 +45,7 @@ mode_yamls : - name : ifu description: Basic three-trace integral-field-unit spectrograph + status: development alias: OBS properties: include_slit: False @@ -49,3 +53,20 @@ mode_yamls : filter_name: "open" yamls: - YAML_mode_ifu.yaml # mode specific effects and properties + +- name: mock_concept_mode + description: Dummy mode to test concept status. + status: concept + +- name: mock_experimental_mode + description: Dummy mode to test experimental status. + status: experimental + +- name: mock_deprecated_mode + description: Dummy mode to test deprecated status without message. + status: deprecated + +- name: mock_deprecated_mode_msg + description: Dummy mode to test deprecated status with message. + status: deprecated + deprecate: This mode is deprecated. diff --git a/scopesim/tests/test_basic_instrument/test_basic_instrument.py b/scopesim/tests/test_basic_instrument/test_basic_instrument.py index 18391bdc..4f305479 100644 --- a/scopesim/tests/test_basic_instrument/test_basic_instrument.py +++ b/scopesim/tests/test_basic_instrument/test_basic_instrument.py @@ -1,4 +1,4 @@ - +# -*- coding: utf-8 -*- import pytest import numpy as np @@ -116,6 +116,7 @@ def test_runs(self): trace_flux = det_im[:, sl].sum() # sum along a trace assert round(trace_flux / spot_flux) == n + @pytest.mark.usefixtures("protect_currsys", "patch_all_mock_paths") class TestSourceImageNotAffected: """ @@ -228,6 +229,57 @@ def test_source_keywords_in_header(self): assert hdr["ESO ATM SEEING"] == opt.cmds["!OBS.psf_fwhm"] +@pytest.mark.usefixtures("protect_currsys", "patch_all_mock_paths") +class TestModeStatus: + def test_concept_mode_init(self): + with pytest.raises(NotImplementedError): + _ = sim.UserCommands(use_instrument="basic_instrument", + set_modes=["mock_concept_mode"]) + + def test_concept_mode_change(self): + cmd = sim.UserCommands(use_instrument="basic_instrument") + with pytest.raises(NotImplementedError): + cmd.set_modes("mock_concept_mode") + + def test_experimental_mode_init(self, caplog): + _ = sim.UserCommands(use_instrument="basic_instrument", + set_modes=["mock_experimental_mode"]) + assert ("Mode 'mock_experimental_mode' is still in experimental stage" + in caplog.text) + + def test_experimental_mode_change(self, caplog): + cmd = sim.UserCommands(use_instrument="basic_instrument") + cmd.set_modes("mock_experimental_mode") + assert ("Mode 'mock_experimental_mode' is still in experimental stage" + in caplog.text) + + def test_deprecated_mode_init(self): + with pytest.raises( + DeprecationWarning, + match="Instrument mode 'mock_deprecated_mode' is deprecated."): + _ = sim.UserCommands(use_instrument="basic_instrument", + set_modes=["mock_deprecated_mode"]) + + def test_deprecated_mode_change(self): + cmd = sim.UserCommands(use_instrument="basic_instrument") + with pytest.raises( + DeprecationWarning, + match="Instrument mode 'mock_deprecated_mode' is deprecated."): + cmd.set_modes("mock_deprecated_mode") + + def test_deprecated_msg_mode_init(self): + with pytest.raises( + DeprecationWarning, match="This mode is deprecated."): + _ = sim.UserCommands(use_instrument="basic_instrument", + set_modes=["mock_deprecated_mode_msg"]) + + def test_deprecated_msg_mode_change(self, caplog): + cmd = sim.UserCommands(use_instrument="basic_instrument") + with pytest.raises( + DeprecationWarning, match="This mode is deprecated."): + cmd.set_modes("mock_deprecated_mode_msg") + + @pytest.fixture(scope="function", name="obs") def basic_opt_observed(): src = st.star(flux=15)