Skip to content

Commit

Permalink
Merge pull request #991 from haddocking/986-validate_parameters_are_n…
Browse files Browse the repository at this point in the history
…ot_incompatible-ignoring-incompatible-value-refactor

Parameter incompatibilities: Increase flexibility in incompatibilities handles.
  • Loading branch information
rvhonorato authored Oct 1, 2024
2 parents 9e84d40 + ba324b9 commit 70f9a23
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 31 deletions.
76 changes: 62 additions & 14 deletions src/haddock/gear/prepare_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@
v_rundir,
validate_defaults_yaml,
)
from haddock.gear.yaml2cfg import read_from_yaml_config
from haddock.gear.yaml2cfg import (
read_from_yaml_config,
find_incompatible_parameters,
)
from haddock.gear.zerofill import zero_fill
from haddock.libs.libfunc import not_none
from haddock.libs.libio import make_writeable_recursive
Expand Down Expand Up @@ -179,16 +182,33 @@ def _read_defaults(module_name, default_only=True):
default.yaml file of the module.
"""
module_name_ = get_module_name(module_name)
pdef = Path(
pdef = gen_defaults_module_param_path(module_name_)
validate_defaults_yaml(pdef)
mod_default_config = read_from_yaml_config(pdef, default_only=default_only)
return mod_default_config


def gen_defaults_module_param_path(module_name_: str) -> Path:
"""Build path to default parameters of a module.
Parameters
----------
module_name_ : str
Name of the module
Returns
-------
Path
Path to the module YAML defaults parameter.
"""
params_defaults_path = Path(
haddock3_source_path,
"modules",
modules_category[module_name_],
module_name_,
"defaults.yaml",
).resolve()
validate_defaults_yaml(pdef)
mod_default_config = read_from_yaml_config(pdef, default_only=default_only)
return mod_default_config
return params_defaults_path


def setup_run(
Expand Down Expand Up @@ -278,7 +298,11 @@ def setup_run(
reference_parameters=ALL_POSSIBLE_GENERAL_PARAMETERS,
)

validate_parameters_are_not_incompatible(general_params)
# Validate there is no incompatible parameters in global parameters
validate_parameters_are_not_incompatible(
general_params,
incompatible_defaults_params,
)

# --extend-run configs do not define the run directory
# in the config file. So we take it from the argument.
Expand Down Expand Up @@ -531,8 +555,7 @@ def validate_modules_names(params: Iterable[str]) -> None:

@with_config_error
def validate_modules_params(modules_params: ParamMap, max_mols: int) -> None:
"""
Validate individual parameters for each module.
"""Validate individual parameters for each module.
Raises
------
Expand All @@ -546,6 +569,21 @@ def validate_modules_params(modules_params: ParamMap, max_mols: int) -> None:
if not defaults:
continue

# Check for parameter incompatibilities
module_incompatibilities = find_incompatible_parameters(
gen_defaults_module_param_path(module_name)
)
try:
validate_parameters_are_not_incompatible(
args,
module_incompatibilities,
)
except ValueError as e:
raise ConfigurationError(
f"An issue was discovered in module [{module_name}]: "
f"{e.args[0]}"
)

if module_name in modules_using_resdic:
confirm_resdic_chainid_length(args)

Expand Down Expand Up @@ -1059,7 +1097,10 @@ def validate_module_names_are_not_misspelled(params: ParamMap) -> None:
return


def validate_parameters_are_not_incompatible(params: ParamMap) -> None:
def validate_parameters_are_not_incompatible(
params: ParamMap,
incompatible_params: ParamMap,
) -> None:
"""
Validate parameters are not incompatible.
Expand All @@ -1071,18 +1112,25 @@ def validate_parameters_are_not_incompatible(params: ParamMap) -> None:
Raises
------
ValueError
If any parameter in `params` is incompatible with another parameter as defined by `incompatible_params`.
If any parameter in `params` is incompatible with another parameter
as defined by `incompatible_params`.
"""
for limiting_param, incompatibilities in incompatible_defaults_params.items():
for limiting_param, incompatibilities in incompatible_params.items():
# Check if the limiting parameter is present in the parameters
if limiting_param in params:
# Point incompatibilities for the value of the limiting parameter
if params[limiting_param] not in incompatibilities.keys():
continue
active_incompatibilities = incompatibilities[params[limiting_param]]
# Check each incompatibility for the limiting parameter
for incompatible_param, incompatible_value in incompatibilities.items():
for incompatible_param, incompatible_value in active_incompatibilities.items():
# Check if the incompatible parameter is present and has the incompatible value
if params.get(incompatible_param) == incompatible_value:
raise ValueError(
f"Parameter `{limiting_param}` is incompatible with `{incompatible_param}={incompatible_value}`."
)
f"Parameter `{limiting_param}` with value "
f"`{params[limiting_param]}` is incompatible with "
f"`{incompatible_param}={incompatible_value}`."
)


def validate_parameters_are_not_misspelled(
Expand Down
3 changes: 2 additions & 1 deletion src/haddock/modules/defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,5 @@ debug:
group: "execution"
explevel: easy
incompatible:
mode: batch
true:
mode: batch
46 changes: 30 additions & 16 deletions tests/test_gear_prepare_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,27 +411,41 @@ def test_param_value_error(defaultparams, key, value):
validate_value(defaultparams, key, value)


def test_validate_parameters_are_not_incompatible(mocker):
"""Test proper logic of test_validate_parameters_are_not_incompatible."""
mocker.patch(
"haddock.gear.prepare_run.incompatible_defaults_params",
{"limiting_parameter": {"incompatible_parameter": "incompatible_value"}},
)

def test_validate_parameters_are_not_incompatible():
"""Test parameter incompatibilities."""
params = {
"limiting_parameter": "",
"limiting_parameter": True,
"incompatible_parameter": "incompatible_value",
}
# Define an incompatible parameter
# when limiting_parameter has value `True`,
# `incompatible_parameter` cannot adopt `incompatible_value`
incompatible_params = {
"limiting_parameter": {
True: {
"incompatible_parameter": "incompatible_value",
}
}
}

# Test 1 successfully fail
with pytest.raises(ValueError):
validate_parameters_are_not_incompatible(params)

params = {
"limiting_parameter": "limiting_value",
"ok_parameter": "ok_value",
validate_parameters_are_not_incompatible(
params,
incompatible_params,
)

# Test 2 - successfully pass
incompatible_params = {
"limiting_parameter": {
False: {
"incompatible_parameter": "incompatible_value",
}
}
}

assert validate_parameters_are_not_incompatible(params) is None
no_return = validate_parameters_are_not_incompatible(
params, incompatible_params
)
assert no_return is None


###################################
Expand Down

0 comments on commit 70f9a23

Please sign in to comment.