diff --git a/.gitignore b/.gitignore index 31847f5c..c848ca3a 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,6 @@ hutch_python/tests/tst/db.txt # unit test artifacts *.sqlite + +# ignore _version.py +_version.py diff --git a/docs/source/upcoming_release_notes/409-Add_option_to_skip_loading_experiment_position_presets.rst b/docs/source/upcoming_release_notes/409-Add_option_to_skip_loading_experiment_position_presets.rst new file mode 100644 index 00000000..c83831e5 --- /dev/null +++ b/docs/source/upcoming_release_notes/409-Add_option_to_skip_loading_experiment_position_presets.rst @@ -0,0 +1,22 @@ +409 Add option to skip loading experiment position presets +################# + +API Changes +----------- +- N/A + +Features +-------- +- Added an option (available in conf.yml) to skip loading experiment motor position presets. + +Bugfixes +-------- +- N/A + +Maintenance +----------- +- N/A + +Contributors +------------ +janeliu-slac diff --git a/docs/source/yaml_files.rst b/docs/source/yaml_files.rst index 3896eaf5..6acf45b6 100644 --- a/docs/source/yaml_files.rst +++ b/docs/source/yaml_files.rst @@ -4,7 +4,8 @@ Yaml Files ``hutch-python`` uses a ``conf.yml`` file for basic configuration. This is a standard yaml file with the following valid keys: ``hutch``, ``db``, ``load``, ``load_level``, ``experiment``, ``obj_config``, -``daq_type``, ``daq_host``, and ``daq_platform``, ``exclude_devices``. +``daq_type``, ``daq_host``, ``daq_platform``, ``exclude_devices``, +``additional_devices``, and ``load_experiment_presets``. hutch @@ -179,7 +180,7 @@ at load time. The list uses the following format: additional_devices ------------- +------------------ The ``additional_devices`` key is optional. This key allows hutch-python to load additional devices that are on a different beamline or in a different hutch/area. The first entry below ``additional_devices`` is a search name, @@ -212,6 +213,45 @@ In the example below hutch-python will load all devices from "tmo_sqr1_search", name: crix_* +load_experiment_presets +----------------------- +The ``load_experiment_presets`` key determines whether hutch-python loads +position presets for the active experiment. It takes a boolean value. + +Position presets are saved locations that can be called by commands such as +``motor.mv_sample()``. Hutch-python supports two types of position presets. + +- Beamline presets are permanent positions shared across experiments. +- Experiment presets are temporary positions associated with the active + experiment. + +.. code-block:: YAML + + load_experiment_presets: false + +When set to ``false``, hutch-python still loads beamline presets. It does not +configure or read the active experiment's preset directory. This can improve +startup performance when the experiment directory contains many preset files. + +When set to ``true``, hutch-python makes both beamline and active experiment +presets available. + +.. code-block:: YAML + + load_experiment_presets: true + +The default is ``true``. Existing configurations that omit this key retain +their current behavior. + +Use the unquoted YAML values ``true`` and ``false``. Quoted values such as +``"false"`` are strings rather than Boolean values and are invalid for this +setting. + +This setting affects only position presets. It does not disable loading the +experiment Python file, questionnaire objects, or other experiment +configuration. + + Full File Example ----------------- diff --git a/hutch_python/constants.py b/hutch_python/constants.py index 053f5d96..e818526a 100644 --- a/hutch_python/constants.py +++ b/hutch_python/constants.py @@ -40,7 +40,8 @@ 'obj_config', 'session_timer', 'exclude_devices', - 'additional_devices' + 'additional_devices', + 'load_experiment_presets' ) NO_LOG_EXCEPTIONS = (KeyboardInterrupt, SystemExit) LOG_DOMAINS = {".pcdsn", ".slac.stanford.edu"} diff --git a/hutch_python/load_conf.py b/hutch_python/load_conf.py index 77de19e5..072b88f7 100644 --- a/hutch_python/load_conf.py +++ b/hutch_python/load_conf.py @@ -582,26 +582,57 @@ def load_conf(conf, hutch_dir=None, args=None): all_objects='Namespace of all loaded objects.', ) - # Install Presets + # Configure saved motor positions. Presets are stored as YAML files under + # the hutch launch directory. if hutch_dir is not None: + # Log preset setup failures without stopping the entire session. with safe_load('position presets'): presets_dir = Path(hutch_dir) / 'presets' + + # Beamline presets are permanent and shared by all experiments. beamline_presets = presets_dir / 'beamline' + + # Preserve existing behavior unless the YAML configuration file + # explicitly disables experiment presets. + load_experiment_presets = conf.get('load_experiment_presets', True) + if not isinstance(load_experiment_presets, bool): + logger.error( + 'Invalid load_experiment_presets value %r; expected "true" ' + 'or "false". Experiment presets will be loaded.', + load_experiment_presets, + ) + load_experiment_presets = True + + # Preset root and permanent beamline directory are required. preset_paths = [presets_dir, beamline_presets] - if experiment is not None: + + # Experiment presets are motor positions associated with the active + # experiment. Configure them only when an experiment is available + # and experiment preset loading is enabled. + if experiment is not None and load_experiment_presets: experiment_presets = presets_dir / raw_expname preset_paths.append(experiment_presets) + + # Create any missing preset directories. for path in preset_paths: if not path.exists(): path.mkdir() path.chmod(0o777) - if experiment is None: - setup_preset_paths(hutch=beamline_presets, - defer_loading=True) + + # Make both permanent beamline presets and active experiment + # presets available. + if experiment is not None and load_experiment_presets: + setup_preset_paths( + hutch=beamline_presets, + exp=experiment_presets, + defer_loading=True, + ) else: - setup_preset_paths(hutch=beamline_presets, - exp=experiment_presets, - defer_loading=True) + # Make only permanent beamline presets available. + setup_preset_paths( + hutch=beamline_presets, + defer_loading=True, + ) # configure objects if obj_config is not None: diff --git a/hutch_python/tests/test_load_conf.py b/hutch_python/tests/test_load_conf.py index d79c3fc1..409a1850 100644 --- a/hutch_python/tests/test_load_conf.py +++ b/hutch_python/tests/test_load_conf.py @@ -34,6 +34,63 @@ def test_file_load(): assert len(Presets._paths) == 2 +def test_experiment_presets_disabled(monkeypatch, tmp_path): + # Check that an active experiment doesn't allow experiment presets to + # be configured when the setting is disabled. + setup_calls = [] + + def mock_setup_preset_paths(**kwargs): + setup_calls.append(kwargs) + monkeypatch.setattr( + 'hutch_python.load_conf.setup_preset_paths', + mock_setup_preset_paths, + ) + monkeypatch.setattr( + 'hutch_python.load_conf.get_qs_objs', + lambda expname: {}, + ) + monkeypatch.setattr( + 'hutch_python.load_conf.get_exp_objs', + lambda expname: SimpleNamespace(), + ) + load_conf( + { + 'hutch': 'tst', + 'experiment': 'tstx010', + 'load_experiment_presets': False, + 'daq_type': 'nodaq', + }, + hutch_dir=tmp_path, + ) + beamline_path = tmp_path / 'presets' / 'beamline' + experiment_path = tmp_path / 'presets' / 'x010' + assert setup_calls == [{ + 'hutch': beamline_path, + 'defer_loading': True, + }] + assert beamline_path.is_dir() + assert not experiment_path.exists() + + +def test_presets_without_experiment(monkeypatch, tmp_path): + setup_calls = [] + monkeypatch.setattr( + 'hutch_python.load_conf.setup_preset_paths', + lambda **kwargs: setup_calls.append(kwargs), + ) + load_conf( + { + 'load_experiment_presets': True, + 'daq_type': 'nodaq', + }, + hutch_dir=tmp_path, + ) + assert setup_calls == [{ + 'hutch': tmp_path / 'presets' / 'beamline', + 'defer_loading': True, + }] + + def test_exp_override(): logger.debug('test_exp_override') set_sim_mode(True)