Skip to content

Commit 5c1ffe3

Browse files
authored
Merge pull request #817 from gpetretto/jobconfig
Jobconfig
2 parents 8d6ae16 + 1a49cf1 commit 5c1ffe3

File tree

3 files changed

+82
-22
lines changed

3 files changed

+82
-22
lines changed

src/jobflow/core/flow.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,7 @@ def update_config(
706706
function_filter: Callable = None,
707707
attributes: list[str] | str = None,
708708
dynamic: bool = True,
709+
dict_mod: bool = False,
709710
):
710711
"""
711712
Update the job config of all Jobs in the Flow.
@@ -728,6 +729,9 @@ def update_config(
728729
dynamic
729730
The updates will be propagated to Jobs/Flows dynamically generated at
730731
runtime.
732+
dict_mod
733+
Use the dict mod language to apply updates. See :obj:`.DictMods` for more
734+
details.
731735
732736
Examples
733737
--------
@@ -767,6 +771,7 @@ def update_config(
767771
function_filter=function_filter,
768772
attributes=attributes,
769773
dynamic=dynamic,
774+
dict_mod=dict_mod,
770775
)
771776

772777
def add_hosts_uuids(

src/jobflow/core/job.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,7 @@ def update_config(
10221022
function_filter: Callable = None,
10231023
attributes: list[str] | str = None,
10241024
dynamic: bool = True,
1025+
dict_mod: bool = False,
10251026
):
10261027
"""
10271028
Update the job config.
@@ -1045,6 +1046,9 @@ def update_config(
10451046
dynamic
10461047
The updates will be propagated to Jobs/Flows dynamically generated at
10471048
runtime.
1049+
dict_mod
1050+
Use the dict mod language to apply updates. See :obj:`.DictMods` for more
1051+
details.
10481052
10491053
Examples
10501054
--------
@@ -1096,12 +1100,23 @@ def update_config(
10961100
At variance, if `dynamic` is set to `False` the `manager_config` option will
10971101
only be set for the `test_job` and not for the generated Jobs.
10981102
"""
1103+
from jobflow.utils.dict_mods import apply_mod
1104+
1105+
if dict_mod and attributes:
1106+
raise ValueError("dict_mod and attributes options cannot be used together")
1107+
1108+
if dict_mod and isinstance(config, JobConfig):
1109+
raise ValueError(
1110+
"If dict_mod is selected the update config cannot be a JobConfig object"
1111+
)
1112+
10991113
if dynamic:
11001114
dict_input = {
11011115
"config": config,
11021116
"name_filter": name_filter,
11031117
"function_filter": function_filter,
11041118
"attributes": attributes,
1119+
"dict_mod": dict_mod,
11051120
}
11061121
self.config_updates.append(dict_input)
11071122

@@ -1119,29 +1134,34 @@ def update_config(
11191134
return
11201135

11211136
# if we get to here then we pass all the filters
1122-
if isinstance(config, dict):
1123-
# convert dict specification to a JobConfig but set the attributes
1124-
if attributes is None:
1125-
attributes = list(config.keys())
1126-
1127-
attributes = [attributes] if isinstance(attributes, str) else attributes
1128-
if not set(attributes).issubset(set(config.keys())):
1129-
raise ValueError(
1130-
"Specified attributes include a key that is not present in the "
1131-
"config dictionary."
1132-
)
1133-
config = JobConfig(**config)
1134-
1135-
if attributes is None:
1136-
# overwrite the whole config
1137-
self.config = config
1137+
if dict_mod:
1138+
conf_dict = self.config.as_dict()
1139+
apply_mod(config, conf_dict)
1140+
self.config = JobConfig.from_dict(conf_dict)
11381141
else:
1139-
# only update the specified attributes
1140-
attributes = [attributes] if isinstance(attributes, str) else attributes
1141-
for attr in attributes:
1142-
if not hasattr(self.config, attr):
1143-
raise ValueError(f"Unknown JobConfig attribute: {attr}")
1144-
setattr(self.config, attr, getattr(config, attr))
1142+
if isinstance(config, dict):
1143+
# convert dict specification to a JobConfig but set the attributes
1144+
if attributes is None:
1145+
attributes = list(config.keys())
1146+
1147+
attributes = [attributes] if isinstance(attributes, str) else attributes
1148+
if not set(attributes).issubset(set(config.keys())):
1149+
raise ValueError(
1150+
"Specified attributes include a key that is not present in the "
1151+
"config dictionary."
1152+
)
1153+
config = JobConfig(**config)
1154+
1155+
if attributes is None:
1156+
# overwrite the whole config
1157+
self.config = config
1158+
else:
1159+
# only update the specified attributes
1160+
attributes = [attributes] if isinstance(attributes, str) else attributes
1161+
for attr in attributes:
1162+
if not hasattr(self.config, attr):
1163+
raise ValueError(f"Unknown JobConfig attribute: {attr}")
1164+
setattr(self.config, attr, getattr(config, attr))
11451165

11461166
def as_dict(self) -> dict:
11471167
"""Serialize the job as a dictionary."""

tests/core/test_job.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,41 @@ def jsm_wrapped(a, b):
13321332
):
13331333
test_job.update_config(new_config_dict, attributes="abc_xyz")
13341334

1335+
# test with dict_mod
1336+
test_job = Job(add)
1337+
with pytest.raises(
1338+
ValueError, match="dict_mod and attributes options cannot be used together"
1339+
):
1340+
test_job.update_config({}, attributes="manager_config", dict_mod=True)
1341+
1342+
with pytest.raises(
1343+
ValueError,
1344+
match="If dict_mod is selected the update config cannot be a JobConfig object",
1345+
):
1346+
test_job.update_config(JobConfig(), dict_mod=True)
1347+
1348+
dict_mod_config_1 = {
1349+
"_set": {
1350+
"manager_config->test->x": 1,
1351+
"resolve_references": True,
1352+
}
1353+
}
1354+
test_job.update_config(dict_mod_config_1, dict_mod=True)
1355+
assert test_job.config.manager_config["test"]["x"] == 1
1356+
assert test_job.config.resolve_references
1357+
1358+
dict_mod_config_2 = {
1359+
"_set": {
1360+
"manager_config->test->y": 2,
1361+
"manager_config->test2->z": 3,
1362+
}
1363+
}
1364+
test_job.update_config(dict_mod_config_2, dict_mod=True)
1365+
assert test_job.config.manager_config["test"]["x"] == 1
1366+
assert test_job.config.manager_config["test"]["y"] == 2
1367+
assert test_job.config.manager_config["test2"]["z"] == 3
1368+
assert test_job.config.resolve_references
1369+
13351370
# test applied dynamic updates
13361371
@dataclass
13371372
class TestMaker(Maker):

0 commit comments

Comments
 (0)