Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions src/fmu/dataio/_workflows/case/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,16 @@ class CaseWorkflowConfig:
global_config_path: Path
fmu_dir: ProjectFMUDirectory | None

def __post_init__(self) -> None:
"""Run validation."""
self.validate()

@property
def casename(self) -> str:
return self.casepath.name

def validate(self) -> None:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the validation of the casepath to this new _resolve_casepath so that we minimize the risk of copying .fmu to an incorrect location.

casepath_str = str(self.casepath)
if not self.casepath.is_absolute():
if casepath_str.startswith("<") and casepath_str.endswith(">"):
raise ValueError(
f"Ert variable for case path is not defined: {self.casepath}"
)
raise ValueError(
f"'casepath' must be an absolute path. Got: {self.casepath}"
)

@classmethod
def from_presim_workflow(
cls,
run_paths: ErtRunpaths,
args: argparse.Namespace,
casepath: Path,
fmu_dir: ProjectFMUDirectory | None = None,
) -> Self:
"""Create an instance from Ert workflow arguments."""
Expand All @@ -67,7 +53,7 @@ def from_presim_workflow(
global_config = load_global_config(config_path)

return cls(
casepath=args.casepath,
casepath=casepath,
ert_config_path=ert_config_path,
register_on_sumo=args.sumo,
verbosity="WARNING",
Expand Down
66 changes: 60 additions & 6 deletions src/fmu/dataio/_workflows/case/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import argparse
import logging
import shutil
import warnings
from pathlib import Path
from typing import TYPE_CHECKING, Final

Expand Down Expand Up @@ -52,16 +53,61 @@
"""

EXAMPLES = """
Create an Ert workflow e.g. called ``ert/bin/workflows/create_case_metadata`` with::
Create an Ert workflow e.g. called ``ert/bin/workflows/xhook_create_case_metadata`` with::

WF_CREATE_CASE_METADATA <casepath> "--sumo"
WF_CREATE_CASE_METADATA "--sumo"

Arguments:
<casepath>: Absolute path to root of the case, typically <SCRATCH>/<USER>/<CASE_DIR>
--sumo: Register case on Sumo

Note that ``<SUMO_CASEPATH>`` must be defined in the Ert config for this workflow to run::

DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>

""" # noqa: E501


def _validate_casepath(casepath: Path) -> Path:
"""Validate that the case path is absolute and defined in the ERT config."""
if not casepath.is_absolute():
casepath_str = str(casepath)
if casepath_str.startswith("<") and casepath_str.endswith(">"):
raise ValueError(f"Ert variable for case path is not defined: {casepath}")
raise ValueError(f"'casepath' must be an absolute path. Got: {casepath}")
return casepath


def _resolve_casepath(run_paths: ErtRunpaths, args: argparse.Namespace) -> Path:
"""Resolve case path, preferring <SUMO_CASEPATH> over argument fallback."""

sumo_casepath = run_paths.substitutions.get("<SUMO_CASEPATH>")

if sumo_casepath:
if args.casepath:
warnings.warn(
"The argument 'casepath' is deprecated. It is no longer used and can "
"safely be removed from WF_CREATE_CASE_METADATA. The case path is now "
"read from the <SUMO_CASEPATH> variable.",
FutureWarning,
)
return _validate_casepath(Path(sumo_casepath))

if args.casepath:
if args.sumo:
raise ValueError(
"Missing required <SUMO_CASEPATH> definition. "
"Define it in your ERT config, for example:\n"
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>"
)
return _validate_casepath(Path(args.casepath))

raise ValueError(
"The case path could not be resolved. Please define the <SUMO_CASEPATH> "
"variable in the ERT config, for example:\n\n "
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>"
)


def _get_ensemble_name(
ensemble: ErtEnsemble,
run_paths: ErtRunpaths,
Expand Down Expand Up @@ -303,7 +349,12 @@ def get_parser() -> argparse.ArgumentParser:
parser.add_argument(
"casepath",
type=Path,
help="Absolute path to the case",
nargs="?",
default=None,
help=(
"Absolute path to the case. If not provided, "
"it is resolved from the <SUMO_CASEPATH> variable."
),
)
parser.add_argument(
"--sumo",
Expand Down Expand Up @@ -374,9 +425,12 @@ def run(
parser = get_parser()
args = parser.parse_args(workflow_args)

maybe_fmu_dir = _copy_fmu_directory(args.casepath)
casepath = _resolve_casepath(run_paths, args)
maybe_fmu_dir = _copy_fmu_directory(casepath)

cfg = CaseWorkflowConfig.from_presim_workflow(run_paths, args, maybe_fmu_dir)
cfg = CaseWorkflowConfig.from_presim_workflow(
run_paths, args, casepath, maybe_fmu_dir
)
_run_workflow(ensemble, run_paths, cfg)


Expand Down
154 changes: 154 additions & 0 deletions tests/test_ert_integration/test_wf_create_case_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,32 @@ def test_create_case_metadata_runs_successfully(
assert fmu_case["tracklog"][0]["user"]["id"] == getpass.getuser()


def test_create_case_metadata_runs_without_arguments(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
"""Workflow should run with no positional arguments."""
workflow_path = (
fmu_snakeoil_project / "ert/bin/workflows/xhook_create_case_metadata"
)
workflow_path.write_text("WF_CREATE_CASE_METADATA", encoding="utf-8")

ert_model_path = fmu_snakeoil_project / "ert/model"
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

add_create_case_workflow(ert_config_path)

expected_fmu_case_yml = (
fmu_snakeoil_project / "scratch/user/snakeoil/share/metadata/fmu_case.yml"
)
assert not expected_fmu_case_yml.exists()

with patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]):
ert.__main__.main()

assert expected_fmu_case_yml.exists()


def test_create_case_metadata_uses_dotfmu_config(
fmu_snakeoil_project_with_dotfmu: Path, monkeypatch: MonkeyPatch
) -> None:
Expand Down Expand Up @@ -244,6 +270,13 @@ def test_create_case_metadata_caseroot_not_defined(
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

# remove the default definition of <SUMO_CASEPATH> from the ert config
ert_config_path.write_text(
ert_config_path.read_text().replace(
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>", ""
)
)

add_create_case_workflow(ert_config_path)

with (
Expand All @@ -253,6 +286,126 @@ def test_create_case_metadata_caseroot_not_defined(
ert.__main__.main()


def test_create_case_metadata_sumo_casepath_not_defined(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
"""Test that a ERT is stopped and that a proper error message is given
if the case path is input as an undefined ERT variable"""

ert_model_path = fmu_snakeoil_project / "ert/model"
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

ert_config_path.write_text(
ert_config_path.read_text().replace(
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>",
"DEFINE <SUMO_CASEPATH> <CASEPATH_NOT_DEFINED>",
)
)

add_create_case_workflow(ert_config_path)

with (
patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]),
pytest.raises(SystemExit, match="Ert variable for case path is not defined"),
):
ert.__main__.main()


def test_create_case_metadata_sumo_casepath_not_absolute(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
"""Test that an error is given if the SUMO_CASEPATH is not an absolute path."""
pathlib.Path(
fmu_snakeoil_project / "ert/bin/workflows/xhook_create_case_metadata"
).write_text(
"WF_CREATE_CASE_METADATA <CASEPATH_NOT_DEFINED>",
encoding="utf-8",
)

ert_model_path = fmu_snakeoil_project / "ert/model"
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

ert_config_path.write_text(
ert_config_path.read_text().replace(
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>",
"DEFINE <SUMO_CASEPATH> relative/path",
)
)

add_create_case_workflow(ert_config_path)

with (
patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]),
pytest.raises(
SystemExit, match="'casepath' must be an absolute path. Got: relative/path"
),
):
ert.__main__.main()


def test_create_case_metadata_fails_if_sumo_enabled_without_sumo_casepath(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
"""When --sumo is enabled, missing <SUMO_CASEPATH> should fail."""
pathlib.Path(
fmu_snakeoil_project / "ert/bin/workflows/xhook_create_case_metadata"
).write_text(
'WF_CREATE_CASE_METADATA "--sumo" <SCRATCH>/<USER>/<CASE_DIR>',
encoding="utf-8",
)

ert_model_path = fmu_snakeoil_project / "ert/model"
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

ert_config_path.write_text(
ert_config_path.read_text().replace(
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>", ""
)
)

add_create_case_workflow(ert_config_path)

with (
patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]),
pytest.raises(SystemExit, match="Missing required <SUMO_CASEPATH> definition"),
):
ert.__main__.main()


def test_create_case_metadata_fails_if_no_casepath_sources(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
"""Missing both <SUMO_CASEPATH> and casepath argument should fail."""
pathlib.Path(
fmu_snakeoil_project / "ert/bin/workflows/xhook_create_case_metadata"
).write_text(
"WF_CREATE_CASE_METADATA",
encoding="utf-8",
)

ert_model_path = fmu_snakeoil_project / "ert/model"
monkeypatch.chdir(ert_model_path)
ert_config_path = ert_model_path / "snakeoil.ert"

# remove the definition of <SUMO_CASEPATH> from the ert config
ert_config_path.write_text(
ert_config_path.read_text().replace(
"DEFINE <SUMO_CASEPATH> <SCRATCH>/<USER>/<CASE_DIR>", ""
)
)

add_create_case_workflow(ert_config_path)

with (
patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]),
pytest.raises(SystemExit, match="The case path could not be resolved"),
):
ert.__main__.main()


def test_create_case_metadata_deprecated_arguments_warn(
fmu_snakeoil_project: Path, monkeypatch: MonkeyPatch
) -> None:
Expand All @@ -272,6 +425,7 @@ def test_create_case_metadata_deprecated_arguments_warn(

with (
patch("sys.argv", ["ert", "test_run", "snakeoil.ert", "--disable-monitoring"]),
pytest.warns(FutureWarning, match="The argument 'casepath' is deprecated"),
pytest.warns(
FutureWarning, match="The argument 'ert_config_path' is deprecated"
),
Expand Down
Loading
Loading