Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameter incompatibilities: Increase flexibility in incompatibilities handles. #991

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:
VGPReys marked this conversation as resolved.
Show resolved Hide resolved
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
Loading