diff --git a/changelog.md b/changelog.md index 9c4d50aea..df38c1a25 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ ### 👌 Minor Improvements: - 👌🎨 Add proper repr for DatasetMapping (#957) +- 👌 Add SavingOptions to save_result API (#966) ### 🩹 Bug fixes @@ -19,6 +20,8 @@ ### 🗑️ Deprecations (due in 0.8.0) +- `glotaran.io.save_result(result, result_path, format_name='legacy')` -> `glotaran.io.save_result(result, Path(result_path) / 'result.yml')` + ### 🚧 Maintenance - 🔧 Improve packaging tooling (#923) diff --git a/glotaran/builtin/io/folder/folder_plugin.py b/glotaran/builtin/io/folder/folder_plugin.py index 58171ef98..0711a80a4 100644 --- a/glotaran/builtin/io/folder/folder_plugin.py +++ b/glotaran/builtin/io/folder/folder_plugin.py @@ -8,11 +8,12 @@ from pathlib import Path from typing import TYPE_CHECKING +from warnings import warn +from glotaran.deprecation import warn_deprecated from glotaran.io import save_dataset -from glotaran.io import save_model from glotaran.io import save_parameters -from glotaran.io import save_scheme +from glotaran.io import save_result from glotaran.io.interface import ProjectIoInterface from glotaran.plugin_system.project_io_registration import SAVING_OPTIONS_DEFAULT from glotaran.plugin_system.project_io_registration import register_project_io @@ -22,7 +23,68 @@ from glotaran.project import Result -@register_project_io(["folder", "legacy"]) +@register_project_io("legacy") +class LegacyProjectIo(ProjectIoInterface): + """Project Io plugin to save result data in a backward compatible manner.""" + + def save_result( + self, + result: Result, + result_path: str, + *, + saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, + ) -> list[str]: + """Save the result to a given folder. + + Warning + ------- + Deprecated use ``glotaran.io.save_result(result, Path(result_path) / 'result.yml')`` + instead. + + Returns a list with paths of all saved items. + The following files are saved if not configured otherwise: + * ``result.md``: The result with the model formatted as markdown text. + * ``result.yml``: Yaml spec file of the result + * ``model.yml``: Model spec file. + * ``scheme.yml``: Scheme spec file. + * ``initial_parameters.csv``: Initially used parameters. + * ``optimized_parameters.csv``: The optimized parameter as csv file. + * ``parameter_history.csv``: Parameter changes over the optimization + * ``{dataset_label}.nc``: The result data for each dataset as NetCDF file. + + Parameters + ---------- + result : Result + Result instance to be saved. + result_path : str + The path to the folder in which to save the result. + saving_options : SavingOptions + Options for saving the the result. + + Returns + ------- + list[str] + List of file paths which were created. + """ + warn_deprecated( + deprecated_qual_name_usage=( + "glotaran.io.save_result(result, result_path, format_name='legacy')" + ), + new_qual_name_usage=( + "glotaran.io.save_result(result, Path(result_path) / 'result.yml')" + ), + to_be_removed_in_version="0.8.0", + ) + + return save_result( + result=result, + result_path=Path(result_path) / "result.yml", + saving_options=saving_options, + allow_overwrite=True, + ) + + +@register_project_io("folder") class FolderProjectIo(ProjectIoInterface): """Project Io plugin to save result data to a folder. @@ -36,18 +98,17 @@ def save_result( result_path: str, *, saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, + used_inside_of_plugin: bool = False, ) -> list[str]: """Save the result to a given folder. Returns a list with paths of all saved items. The following files are saved if not configured otherwise: - * `result.md`: The result with the model formatted as markdown text. - * `model.yml`: Model spec file. - * `scheme.yml`: Scheme spec file. - * `initial_parameters.csv`: Initially used parameters. - * `optimized_parameters.csv`: The optimized parameter as csv file. - * `parameter_history.csv`: Parameter changes over the optimization - * `{dataset_label}.nc`: The result data for each dataset as NetCDF file. + * ``result.md``: The result with the model formatted as markdown text. + * ``initial_parameters.csv``: Initially used parameters. + * ``optimized_parameters.csv``: The optimized parameter as csv file. + * ``parameter_history.csv``: Parameter changes over the optimization + * ``{dataset_label}.nc``: The result data for each dataset as NetCDF file. Note ---- @@ -62,7 +123,9 @@ def save_result( The path to the folder in which to save the result. saving_options : SavingOptions Options for saving the the result. - + used_inside_of_plugin: bool + Denote that this plugin is used from inside another plugin, + if false a user warning will be thrown. , by default False Returns ------- @@ -74,6 +137,15 @@ def save_result( ValueError If ``result_path`` is a file. """ + if used_inside_of_plugin is not True: + warn( + UserWarning( + "The folder plugin is only intended for internal use by other plugins " + "as quick way to save most of the files. The saved result will be incomplete, " + "thus it is not recommended to be used directly." + ) + ) + result_folder = Path(result_path) if result_folder.is_file(): raise ValueError(f"The path '{result_folder}' is not a directory.") @@ -85,10 +157,6 @@ def save_result( report_path.write_text(str(result.markdown())) paths.append(report_path.as_posix()) - model_path = result_folder / "model.yml" - save_model(result.scheme.model, model_path, allow_overwrite=True) - paths.append(model_path.as_posix()) - initial_parameters_path = f"initial_parameters.{saving_options.parameter_format}" save_parameters( result.scheme.parameters, @@ -107,16 +175,14 @@ def save_result( ) paths.append((result_folder / optimized_parameters_path).as_posix()) - scheme_path = result_folder / "scheme.yml" - save_scheme(result.scheme, scheme_path, allow_overwrite=True) - paths.append(scheme_path.as_posix()) - parameter_history_path = result_folder / "parameter_history.csv" result.parameter_history.to_csv(parameter_history_path) paths.append(parameter_history_path.as_posix()) for label, dataset in result.data.items(): data_path = result_folder / f"{label}.{saving_options.data_format}" + if saving_options.data_filter is not None: + dataset = dataset[saving_options.data_filter] save_dataset( dataset, data_path, diff --git a/glotaran/builtin/io/folder/test/test_folder_plugin.py b/glotaran/builtin/io/folder/test/test_folder_plugin.py index 50ba60fd4..0dc2c9775 100644 --- a/glotaran/builtin/io/folder/test/test_folder_plugin.py +++ b/glotaran/builtin/io/folder/test/test_folder_plugin.py @@ -6,6 +6,7 @@ import pytest from glotaran.analysis.optimize import optimize +from glotaran.deprecation import GlotaranApiDeprecationWarning from glotaran.io import save_result from glotaran.project.result import Result from glotaran.testing.simulated_data.sequential_spectral_decay import SCHEME @@ -27,19 +28,27 @@ def test_save_result_folder( """Check all files exist.""" result_dir = tmp_path / "testresult" - save_paths = save_result( - result_path=str(result_dir), format_name=format_name, result=dummy_result - ) + assert not result_dir.exists() + with pytest.warns(UserWarning) as record: + save_paths = save_result( + result_path=str(result_dir), format_name=format_name, result=dummy_result + ) + + assert len(record) == 1 + if format_name == "legacy": + assert record[0].category == GlotaranApiDeprecationWarning + else: + assert record[0].category == UserWarning wanted_files = [ "result.md", - "scheme.yml", - "model.yml", "initial_parameters.csv", "optimized_parameters.csv", "parameter_history.csv", "dataset_1.nc", ] + if format_name == "legacy": + wanted_files += ["scheme.yml", "model.yml", "result.yml"] for wanted in wanted_files: assert (result_dir / wanted).exists() assert (result_dir / wanted).as_posix() in save_paths @@ -53,13 +62,14 @@ def test_save_result_folder_error_path_is_file( ): """Raise error if result_path is a file without extension and overwrite is true.""" - result_dir = tmp_path / "testresult" + result_dir = tmp_path / "testresulterror" 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, - ) + with pytest.warns(UserWarning): + 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/builtin/io/yml/test/test_save_result.py b/glotaran/builtin/io/yml/test/test_save_result.py index d7946462e..94e546a22 100644 --- a/glotaran/builtin/io/yml/test/test_save_result.py +++ b/glotaran/builtin/io/yml/test/test_save_result.py @@ -16,8 +16,8 @@ @pytest.fixture(scope="session") def dummy_result(): """Dummy result for testing.""" + print(SCHEME.data["dataset_1"]) scheme = replace(SCHEME, maximum_number_function_evaluations=1) - print(scheme.data["dataset_1"]) yield optimize(scheme, raise_exception=True) @@ -58,4 +58,6 @@ def test_save_result_yml( assert (result_dir / "dataset_1.nc").exists() # We can't check equality due to numerical fluctuations - assert expected in (result_dir / "result.yml").read_text() + got = (result_dir / "result.yml").read_text() + print(got) + assert expected in got diff --git a/glotaran/builtin/io/yml/yml.py b/glotaran/builtin/io/yml/yml.py index f35c42864..8abfb8b8f 100644 --- a/glotaran/builtin/io/yml/yml.py +++ b/glotaran/builtin/io/yml/yml.py @@ -8,9 +8,13 @@ from glotaran.deprecation.modules.builtin_io_yml import model_spec_deprecations from glotaran.deprecation.modules.builtin_io_yml import scheme_spec_deprecations +from glotaran.io import SAVING_OPTIONS_DEFAULT from glotaran.io import ProjectIoInterface +from glotaran.io import SavingOptions from glotaran.io import register_project_io +from glotaran.io import save_model from glotaran.io import save_result +from glotaran.io import save_scheme from glotaran.model import Model from glotaran.parameter import ParameterGroup from glotaran.project import Result @@ -130,8 +134,24 @@ def load_result(self, result_path: str) -> Result: spec = self._load_yml(result_path) return fromdict(Result, spec, folder=Path(result_path).parent) - def save_result(self, result: Result, result_path: str): - """Write a :class:`Result` instance to a spec file. + def save_result( + self, + result: Result, + result_path: str, + saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, + ) -> list[str]: + """Write a :class:`Result` instance to a spec file and data files. + + Returns a list with paths of all saved items. + The following files are saved if not configured otherwise: + * ``result.md``: The result with the model formatted as markdown text. + * ``result.yml``: Yaml spec file of the result + * ``model.yml``: Model spec file. + * ``scheme.yml``: Scheme spec file. + * ``initial_parameters.csv``: Initially used parameters. + * ``optimized_parameters.csv``: The optimized parameter as csv file. + * ``parameter_history.csv``: Parameter changes over the optimization + * ``{dataset_label}.nc``: The result data for each dataset as NetCDF file. Parameters ---------- @@ -139,10 +159,37 @@ def save_result(self, result: Result, result_path: str): :class:`Result` instance to write. result_path : str | PathLike[str] Path to write the result data to. + saving_options : SavingOptions + Options for saving the the result. + + Returns + ------- + list[str] + List of file paths which were created. """ - save_result(result, Path(result_path).parent.as_posix(), format_name="folder") - result_dict = asdict(result, folder=Path(result_path).parent) + result_folder = Path(result_path).parent + paths = save_result( + result, + result_folder, + format_name="folder", + saving_options=saving_options, + allow_overwrite=True, + used_inside_of_plugin=True, + ) + + model_path = result_folder / "model.yml" + save_model(result.scheme.model, model_path, allow_overwrite=True) + paths.append(model_path.as_posix()) + + scheme_path = result_folder / "scheme.yml" + save_scheme(result.scheme, scheme_path, allow_overwrite=True) + paths.append(scheme_path.as_posix()) + + result_dict = asdict(result, folder=result_folder) write_dict(result_dict, file_name=result_path) + paths.append(result_path) + + return paths def _load_yml(self, file_name: str) -> dict[str, Any]: yaml = YAML() diff --git a/glotaran/deprecation/__init__.py b/glotaran/deprecation/__init__.py index 1a14e8194..1b081ac5c 100644 --- a/glotaran/deprecation/__init__.py +++ b/glotaran/deprecation/__init__.py @@ -1,7 +1,20 @@ """Deprecation helpers and place to put deprecated implementations till removing.""" +from glotaran.deprecation.deprecation_utils import GlotaranApiDeprecationWarning +from glotaran.deprecation.deprecation_utils import GlotaranDeprectedApiError from glotaran.deprecation.deprecation_utils import deprecate from glotaran.deprecation.deprecation_utils import deprecate_dict_entry from glotaran.deprecation.deprecation_utils import deprecate_module_attribute from glotaran.deprecation.deprecation_utils import deprecate_submodule from glotaran.deprecation.deprecation_utils import raise_deprecation_error from glotaran.deprecation.deprecation_utils import warn_deprecated + +__all__ = [ + "deprecate", + "deprecate_dict_entry", + "deprecate_module_attribute", + "deprecate_submodule", + "raise_deprecation_error", + "warn_deprecated", + "GlotaranApiDeprecationWarning", + "GlotaranDeprectedApiError", +] diff --git a/glotaran/io/__init__.py b/glotaran/io/__init__.py index 343456a92..ef3e254ed 100644 --- a/glotaran/io/__init__.py +++ b/glotaran/io/__init__.py @@ -6,8 +6,11 @@ reexports functions from the pluginsystem from a common place. """ +from glotaran.io.interface import SAVING_OPTIONS_DEFAULT +from glotaran.io.interface import SAVING_OPTIONS_MINIMAL from glotaran.io.interface import DataIoInterface from glotaran.io.interface import ProjectIoInterface +from glotaran.io.interface import SavingOptions from glotaran.io.prepare_dataset import prepare_time_trace_dataset from glotaran.plugin_system.data_io_registration import data_io_plugin_table from glotaran.plugin_system.data_io_registration import get_dataloader @@ -17,9 +20,6 @@ from glotaran.plugin_system.data_io_registration import save_dataset from glotaran.plugin_system.data_io_registration import set_data_plugin from glotaran.plugin_system.data_io_registration import show_data_io_method_help -from glotaran.plugin_system.project_io_registration import SAVING_OPTIONS_DEFAULT -from glotaran.plugin_system.project_io_registration import SAVING_OPTIONS_MINIMAL -from glotaran.plugin_system.project_io_registration import SavingOptions from glotaran.plugin_system.project_io_registration import get_project_io_method from glotaran.plugin_system.project_io_registration import load_model from glotaran.plugin_system.project_io_registration import load_parameters diff --git a/glotaran/io/interface.py b/glotaran/io/interface.py index f6c5e5019..a151e5af8 100644 --- a/glotaran/io/interface.py +++ b/glotaran/io/interface.py @@ -12,10 +12,12 @@ from __future__ import annotations +from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Callable + from typing import Literal from typing import Union import xarray as xr @@ -29,6 +31,20 @@ DataSaver = Callable[[str, Union[xr.Dataset, xr.DataArray]], None] +@dataclass +class SavingOptions: + """A collection of options for result saving.""" + + data_filter: list[str] | None = None + data_format: Literal["nc"] = "nc" + parameter_format: Literal["csv"] = "csv" + report: bool = True + + +SAVING_OPTIONS_DEFAULT = SavingOptions() +SAVING_OPTIONS_MINIMAL = SavingOptions(data_filter=["fitted_data", "residual"], report=False) + + class DataIoInterface: """Baseclass for Data IO plugins.""" @@ -218,7 +234,13 @@ def load_result(self, result_path: str) -> Result: """ raise NotImplementedError(f"Cannot read result with format {self.format!r}") - def save_result(self, result: Result, result_path: str) -> list[str] | None: + def save_result( + self, + result: Result, + result_path: str, + *, + saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, + ) -> list[str]: """Save a Result instance to a spec file (**NOT IMPLEMENTED**). Parameters @@ -227,6 +249,8 @@ def save_result(self, result: Result, result_path: str) -> list[str] | None: Result instance to save to specs file. result_path : str Path to write the result data to. + saving_options : SavingOptions + Options for the saved result. .. # noqa: DAR101 diff --git a/glotaran/plugin_system/io_plugin_utils.py b/glotaran/plugin_system/io_plugin_utils.py index 5bf7c311e..3a0ae6f5d 100644 --- a/glotaran/plugin_system/io_plugin_utils.py +++ b/glotaran/plugin_system/io_plugin_utils.py @@ -56,10 +56,10 @@ def inferr_file_format( return file_format.lstrip(".") if allow_folder: - return "folder" + return "legacy" else: raise ValueError( - f"Cannot determine format of file {file_path!r}, " "please provide an explicit format." + f"Cannot determine format of file {file_path!r}, please provide an explicit format." ) diff --git a/glotaran/plugin_system/project_io_registration.py b/glotaran/plugin_system/project_io_registration.py index 6ca838b35..2ac580188 100644 --- a/glotaran/plugin_system/project_io_registration.py +++ b/glotaran/plugin_system/project_io_registration.py @@ -8,14 +8,15 @@ """ from __future__ import annotations -from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING from typing import TypeVar from tabulate import tabulate +from glotaran.io.interface import SAVING_OPTIONS_DEFAULT from glotaran.io.interface import ProjectIoInterface +from glotaran.io.interface import SavingOptions from glotaran.plugin_system.base_registry import __PluginRegistry from glotaran.plugin_system.base_registry import add_instantiated_plugin_to_registry from glotaran.plugin_system.base_registry import get_method_from_plugin @@ -54,20 +55,6 @@ Literal["save_result"], ) - -@dataclass -class SavingOptions: - """A collection of options for result saving.""" - - data_filter: list[str] | None = None - data_format: Literal["nc"] = "nc" - parameter_format: Literal["csv"] = "csv" - report: bool = True - - -SAVING_OPTIONS_DEFAULT = SavingOptions() -SAVING_OPTIONS_MINIMAL = SavingOptions(data_filter=["fitted_data", "residual"], report=False) - PROJECT_IO_METHODS = ( "load_model", "save_model", @@ -447,8 +434,9 @@ def save_result( *, allow_overwrite: bool = False, update_source_path: bool = True, + saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, **kwargs: Any, -) -> list[str] | None: +) -> list[str]: """Write a :class:`Result` instance to a spec file. Parameters @@ -465,6 +453,8 @@ def save_result( update_source_path: bool Whether or not to update the ``source_path`` attribute to ``result_path`` when saving. by default True + saving_options : SavingOptions + Options for the saved result. **kwargs : Any Additional keyword arguments passes to the ``save_result`` implementation of the project io plugin. @@ -481,6 +471,7 @@ def save_result( paths = io.save_result( # type: ignore[call-arg] result_path=Path(result_path).as_posix(), result=result, + saving_options=saving_options, **kwargs, ) if update_source_path is True: diff --git a/glotaran/plugin_system/test/test_io_plugin_utils.py b/glotaran/plugin_system/test/test_io_plugin_utils.py index b8e4b40a0..e612cbb3a 100644 --- a/glotaran/plugin_system/test/test_io_plugin_utils.py +++ b/glotaran/plugin_system/test/test_io_plugin_utils.py @@ -54,12 +54,12 @@ def test_inferr_file_format_no_extension(tmp_path: 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.""" + """If there is no extension, return legacy.""" file_path = tmp_path / "dummy" if is_file: file_path.touch() - assert inferr_file_format(file_path, allow_folder=True) == "folder" + assert inferr_file_format(file_path, allow_folder=True) == "legacy" def test_inferr_file_format_none_existing_file(): diff --git a/glotaran/plugin_system/test/test_project_io_registration.py b/glotaran/plugin_system/test/test_project_io_registration.py index ae0c1c0f8..f9b69d246 100644 --- a/glotaran/plugin_system/test/test_project_io_registration.py +++ b/glotaran/plugin_system/test/test_project_io_registration.py @@ -12,6 +12,8 @@ from glotaran.parameter import ParameterGroup from glotaran.plugin_system.base_registry import PluginOverwriteWarning from glotaran.plugin_system.base_registry import __PluginRegistry +from glotaran.plugin_system.project_io_registration import SAVING_OPTIONS_DEFAULT +from glotaran.plugin_system.project_io_registration import SavingOptions from glotaran.plugin_system.project_io_registration import get_project_io from glotaran.plugin_system.project_io_registration import get_project_io_method from glotaran.plugin_system.project_io_registration import is_known_project_format @@ -116,6 +118,8 @@ def save_result( # type:ignore[override] self, result: Result, result_path: StrOrPath, + *, + saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT, **kwargs: Any, ): result.func_args.update( # type:ignore[attr-defined] diff --git a/glotaran/project/result.py b/glotaran/project/result.py index 9fb6bd9bf..af36836ba 100644 --- a/glotaran/project/result.py +++ b/glotaran/project/result.py @@ -15,6 +15,8 @@ from tabulate import tabulate from glotaran.deprecation import deprecate +from glotaran.io import SAVING_OPTIONS_DEFAULT +from glotaran.io import SavingOptions from glotaran.io import load_result from glotaran.io import save_result from glotaran.model import Model @@ -246,13 +248,17 @@ def __str__(self) -> str: """Overwrite of ``__str__``.""" return str(self.markdown(with_model=False)) - def save(self, path: str) -> list[str]: + def save( + self, path: StrOrPath, saving_options: SavingOptions = SAVING_OPTIONS_DEFAULT + ) -> list[str]: """Save the result to given folder. Parameters ---------- - path : str + path : StrOrPath The path to the folder in which to save the result. + saving_options : SavingOptions + Options for the saved result. Returns ------- @@ -261,7 +267,13 @@ def save(self, path: str) -> list[str]: """ return cast( List[str], - save_result(result_path=path, result=self, format_name="folder", allow_overwrite=True), + save_result( + result_path=path, + result=self, + format_name="yaml", + allow_overwrite=True, + saving_options=saving_options, + ), ) def recreate(self) -> Result: diff --git a/glotaran/project/test/test_result.py b/glotaran/project/test/test_result.py index 7f9f5f80e..a4e7eb0fb 100644 --- a/glotaran/project/test/test_result.py +++ b/glotaran/project/test/test_result.py @@ -1,9 +1,15 @@ from __future__ import annotations +from pathlib import Path + import pytest +import xarray as xr from IPython.core.formatters import format_display_data from glotaran.analysis.optimize import optimize +from glotaran.io import SAVING_OPTIONS_DEFAULT +from glotaran.io import SAVING_OPTIONS_MINIMAL +from glotaran.io import SavingOptions from glotaran.project.result import Result from glotaran.testing.simulated_data.sequential_spectral_decay import SCHEME @@ -27,3 +33,56 @@ def test_result_ipython_rendering(dummy_result: Result): assert "text/markdown" in rendered_markdown_return assert rendered_markdown_return["text/markdown"].startswith("| Optimization Result") + + +def test_get_scheme(dummy_result: Result): + scheme = dummy_result.get_scheme() + assert "residual" not in dummy_result.scheme.data["dataset_1"] + assert "residual" not in scheme.data["dataset_1"] + assert all(scheme.parameters.to_dataframe() != dummy_result.scheme.parameters.to_dataframe()) + assert all( + scheme.parameters.to_dataframe() == dummy_result.optimized_parameters.to_dataframe() + ) + + +@pytest.mark.parametrize("saving_options", [SAVING_OPTIONS_MINIMAL, SAVING_OPTIONS_DEFAULT]) +def test_save_result(tmp_path: Path, saving_options: SavingOptions, dummy_result: Result): + result_path = tmp_path / "test_result" + dummy_result.save(result_path / "glotaran_result.yml", saving_options=saving_options) + files_must_exist = [ + "glotaran_result.yml", + "scheme.yml", + "model.yml", + "initial_parameters.csv", + "optimized_parameters.csv", + "parameter_history.csv", + "dataset_1.nc", + ] + files_must_not_exist = [] + if saving_options.report: + files_must_exist.append("result.md") + else: + files_must_not_exist.append("result.md") + + for file in files_must_exist: + assert (result_path / file).exists() + + for file in files_must_not_exist: + assert not (result_path / file).exists() + + dataset_path = result_path / "dataset_1.nc" + assert dataset_path.exists() + dataset = xr.open_dataset(dataset_path) + print(dataset) + if saving_options.data_filter is not None: + assert len(saving_options.data_filter) == len(dataset) + assert all(d in dataset for d in saving_options.data_filter) + + +def test_recreate(dummy_result): + recreated_result = dummy_result.recreate() + assert recreated_result.success + + +def test_verify(dummy_result): + assert dummy_result.verify()