diff --git a/glotaran/builtin/io/folder/folder_plugin.py b/glotaran/builtin/io/folder/folder_plugin.py index 335b2e46c..56f2a858f 100644 --- a/glotaran/builtin/io/folder/folder_plugin.py +++ b/glotaran/builtin/io/folder/folder_plugin.py @@ -47,13 +47,13 @@ def save_result(self, result_path: str, result: Result) -> list[str]: Raises ------ - OSError + ValueError If the folder to save the result in doesn't exist or couldn't be created. """ if not os.path.exists(result_path): os.makedirs(result_path) - elif not os.path.isdir(result_path): - raise OSError(f"The path '{result_path}' is not a directory.") + if not os.path.isdir(result_path): + raise ValueError(f"The path '{result_path}' is not a directory.") paths = [] diff --git a/glotaran/builtin/io/folder/test/test_folder_plugin.py b/glotaran/builtin/io/folder/test/test_folder_plugin.py index 16a6c387e..f42de5a68 100644 --- a/glotaran/builtin/io/folder/test/test_folder_plugin.py +++ b/glotaran/builtin/io/folder/test/test_folder_plugin.py @@ -10,6 +10,7 @@ from glotaran.analysis.test.models import ThreeDatasetDecay as suite from glotaran.io import save_result from glotaran.project import Scheme +from glotaran.project.result import Result if TYPE_CHECKING: from typing import Literal @@ -17,9 +18,10 @@ from py.path import local as TmpDir -@pytest.mark.parametrize("format_names", ("folder", "pyglotaran-legacy")) -def test_save_result_folder(tmpdir: TmpDir, format_name: Literal["folder", "pyglotaran-legacy"]): - """Check all files exist.""" +@pytest.fixture(scope="module") +def dummy_result(): + """Dummy result for testing.""" + model = suite.model model.is_grouped = False @@ -41,13 +43,38 @@ def test_save_result_folder(tmpdir: TmpDir, format_name: Literal["folder", "pygl maximum_number_function_evaluations=1, ) - result = optimize(scheme) + yield optimize(scheme) + + +@pytest.mark.parametrize("format_name", ("folder", "pyglotaran-legacy")) +def test_save_result_folder( + tmpdir: TmpDir, dummy_result: Result, format_name: Literal["folder", "pyglotaran-legacy"] +): + """Check all files exist.""" result_dir = Path(tmpdir / "testresult") - save_result(result_path=str(result_dir), format_name=format_name, result=result) + save_result(result_path=str(result_dir), format_name=format_name, result=dummy_result) assert (result_dir / "result.md").exists() assert (result_dir / "optimized_parameters.csv").exists() assert (result_dir / "dataset1.nc").exists() assert (result_dir / "dataset2.nc").exists() assert (result_dir / "dataset3.nc").exists() + + +@pytest.mark.parametrize("format_name", ("folder", "pyglotaran-legacy")) +def test_save_result_folder_error_path_is_file( + tmpdir: TmpDir, dummy_result: Result, format_name: Literal["folder", "pyglotaran-legacy"] +): + """Raise error if result_path is a file without extension and overwrite is true.""" + + result_dir = Path(tmpdir / "testresult") + result_dir.touch() + + with pytest.raises(ValueError, match="The path '.+?' is not a directory."): + save_result( + result_path=str(result_dir), + format_name=format_name, + result=dummy_result, + allow_overwrite=True, + ) diff --git a/glotaran/plugin_system/io_plugin_utils.py b/glotaran/plugin_system/io_plugin_utils.py index 543f1e2de..5b6da29ea 100644 --- a/glotaran/plugin_system/io_plugin_utils.py +++ b/glotaran/plugin_system/io_plugin_utils.py @@ -15,7 +15,9 @@ DecoratedFunc = TypeVar("DecoratedFunc", bound=Callable[..., Any]) # decorated function -def inferr_file_format(file_path: str | os.PathLike[str], *, needs_to_exist: bool = True) -> str: +def inferr_file_format( + file_path: str | os.PathLike[str], *, needs_to_exist: bool = True, allow_folder=False +) -> str: """Inferr format of a file if it exists. Parameters @@ -25,6 +27,9 @@ def inferr_file_format(file_path: str | os.PathLike[str], *, needs_to_exist: boo needs_to_exist : bool Whether or not a file need to exists for an successful format inferring. While write functions don't need the file to exists, load functions do. + allow_folder: bool + Whether or not to allow the format to be ``folder``. + This is only used in ``save_result``. Returns ------- @@ -38,14 +43,18 @@ def inferr_file_format(file_path: str | os.PathLike[str], *, needs_to_exist: boo ValueError If file has no extension. """ - if not os.path.isfile(file_path) and needs_to_exist: + if not os.path.isfile(file_path) and needs_to_exist and not allow_folder: raise ValueError(f"There is no file {file_path!r}.") _, file_format = os.path.splitext(file_path) if file_format == "": - raise ValueError( - f"Cannot determine format of file {file_path!r}, please provide an explicit format." - ) + if allow_folder: + return "folder" + else: + raise ValueError( + f"Cannot determine format of file {file_path!r}, " + "please provide an explicit format." + ) else: return file_format.lstrip(".") diff --git a/glotaran/plugin_system/project_io_registration.py b/glotaran/plugin_system/project_io_registration.py index 78f61aa69..e862e9eeb 100644 --- a/glotaran/plugin_system/project_io_registration.py +++ b/glotaran/plugin_system/project_io_registration.py @@ -368,7 +368,9 @@ def save_result( of the project io plugin. """ protect_from_overwrite(result_path, allow_overwrite=allow_overwrite) - io = get_project_io(format_name or inferr_file_format(result_path, needs_to_exist=False)) + io = get_project_io( + format_name or inferr_file_format(result_path, needs_to_exist=False, allow_folder=True) + ) io.save_result( # type: ignore[call-arg] result_path=result_path, result=result, diff --git a/glotaran/plugin_system/test/test_io_plugin_utils.py b/glotaran/plugin_system/test/test_io_plugin_utils.py index 80fd45f09..b8e4b40a0 100644 --- a/glotaran/plugin_system/test/test_io_plugin_utils.py +++ b/glotaran/plugin_system/test/test_io_plugin_utils.py @@ -52,6 +52,16 @@ def test_inferr_file_format_no_extension(tmp_path: Path): inferr_file_format(file_path) +@pytest.mark.parametrize("is_file", (True, False)) +def test_inferr_file_format_allow_folder(tmp_path: Path, is_file: bool): + """If there is no extension, return folder.""" + file_path = tmp_path / "dummy" + if is_file: + file_path.touch() + + assert inferr_file_format(file_path, allow_folder=True) == "folder" + + def test_inferr_file_format_none_existing_file(): """Raise error if file does not exists.""" with pytest.raises(ValueError, match="There is no file "):