neural-amp-modeler

Neural network emulator for guitar amplifiers
Log | Files | Refs | README | LICENSE

commit 26fdad726c47e0035c79a395634ec57b4c427f0c
parent 17273de0bd13eb691dbf7416c3140b43e11535ac
Author: Steven Atkinson <steven@atkinson.mn>
Date:   Tue, 17 Sep 2024 09:40:46 -0700

[BUGFIX] Handle settings on read-only filesystem (#465)

* Handle settings on read-only filesystems

* Cleanup
Diffstat:
Mnam/train/core.py | 13++++++-------
Mnam/train/gui/_resources/settings.py | 35++++++++++++++++++++++++++++++-----
Atests/test_nam/test_train/test_gui/__init__.py | 3+++
Rtests/test_nam/test_train/test_gui.py -> tests/test_nam/test_train/test_gui/test_main.py | 0
Atests/test_nam/test_train/test_gui/test_resources/__init__.py | 0
Atests/test_nam/test_train/test_gui/test_resources/test_settings.py | 44++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 83 insertions(+), 12 deletions(-)

diff --git a/nam/train/core.py b/nam/train/core.py @@ -982,7 +982,6 @@ def _get_configs( batch_size: int, fit_mrstft: bool, ): - data_config = _get_data_config( input_version=input_version, input_path=input_path, @@ -1564,13 +1563,13 @@ def validate_data( for split in Split: try: init_dataset(data_config, split) - pytorch_data_split_validation_dict[split.value] = ( - _PyTorchDataSplitValidation(passed=True, msg=None) - ) + pytorch_data_split_validation_dict[ + split.value + ] = _PyTorchDataSplitValidation(passed=True, msg=None) except DataError as e: - pytorch_data_split_validation_dict[split.value] = ( - _PyTorchDataSplitValidation(passed=False, msg=str(e)) - ) + pytorch_data_split_validation_dict[ + split.value + ] = _PyTorchDataSplitValidation(passed=False, msg=str(e)) pytorch_data_validation = _PyTorchDataValidation( passed=all(v.passed for v in pytorch_data_split_validation_dict.values()), **pytorch_data_split_validation_dict, diff --git a/nam/train/gui/_resources/settings.py b/nam/train/gui/_resources/settings.py @@ -42,13 +42,38 @@ def _get_settings() -> dict: """ Make sure that ./settings.json exists; if it does, then read it. If not, empty dict. """ - if not _SETTINGS_JSON_PATH.exists(): - _write_settings({}) - with open(_SETTINGS_JSON_PATH, "r") as fp: - return json.load(fp) + return dict() + else: + with open(_SETTINGS_JSON_PATH, "r") as fp: + return json.load(fp) + + +class _WriteSettings(object): + def __init__(self): + self._oserror = False + + def __call__(self, *args, **kwargs): + if self._oserror: + return + # Try-catch for Issue 448 + try: + return _write_settings_unsafe(*args, **kwargs) + except OSError as e: + if "Read-only filesystem" in str(e): + print( + "Failed to write settings--NAM appears to be installed to a " + "read-only filesystem. This is discouraged; consider installing to " + "a location with user-level access." + ) + self._oserror = True + else: + raise e + + +_write_settings = _WriteSettings() -def _write_settings(obj: dict): +def _write_settings_unsafe(obj: dict): with open(_SETTINGS_JSON_PATH, "w") as fp: json.dump(obj, fp, indent=4) diff --git a/tests/test_nam/test_train/test_gui/__init__.py b/tests/test_nam/test_train/test_gui/__init__.py @@ -0,0 +1,3 @@ +# File: __init__.py +# Created Date: Tuesday September 17th 2024 +# Author: Steven Atkinson (steven@atkinson.mn) diff --git a/tests/test_nam/test_train/test_gui.py b/tests/test_nam/test_train/test_gui/test_main.py diff --git a/tests/test_nam/test_train/test_gui/test_resources/__init__.py b/tests/test_nam/test_train/test_gui/test_resources/__init__.py diff --git a/tests/test_nam/test_train/test_gui/test_resources/test_settings.py b/tests/test_nam/test_train/test_gui/test_resources/test_settings.py @@ -0,0 +1,44 @@ +# File: test_resources.py +# Created Date: Tuesday September 17th 2024 +# Author: Steven Atkinson (steven@atkinson.mn) + +from contextlib import contextmanager +from pathlib import Path + +import pytest + +from nam.train.gui._resources import settings + + +class TestReadOnly(object): + """ + Issue 448 + """ + + @pytest.mark.parametrize("path_key", tuple(pk for pk in settings.PathKey)) + def test_get_last_path(self, path_key: settings.PathKey): + with self._mock_read_only(): + last_path = settings.get_last_path(path_key) + assert last_path is None or isinstance(last_path, Path) + + @pytest.mark.parametrize("path_key", tuple(pk for pk in settings.PathKey)) + def test_set_last_path(self, path_key: settings.PathKey): + path = Path(__file__).parent / Path("dummy.txt") + with self._mock_read_only(): + settings.set_last_path(path_key, path) + + @contextmanager + def _mock_read_only(self): + def write_settings(*args, **kwargs): + raise OSError("Read-only filesystem") + + try: + tmp = settings._write_settings_unsafe + settings._write_settings_unsafe = write_settings + yield + finally: + settings._write_settings_unsafe = tmp + + +if __name__ == "__main__": + pytest.main()