Skip to content

Commit 7338b19

Browse files
bpiwowarclaude
andcommitted
fix: recurse validate into list, dict, and set parameters
Previously, validate() only checked direct Config values but skipped Config objects nested inside collections, leaving required parameters unchecked. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 18479c6 commit 7338b19

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/experimaestro/core/objects/config.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,20 @@ def postprocess(self, stub, config: Config, values):
434434

435435
return TagFinder()(self.pyobject)
436436

437+
def _validate_value(self, value):
438+
"""Recursively validate a value, handling Config, list, dict, and set"""
439+
if isinstance(value, Config):
440+
value.__xpm__.validate()
441+
elif isinstance(value, list):
442+
for item in value:
443+
self._validate_value(item)
444+
elif isinstance(value, dict):
445+
for item in value.values():
446+
self._validate_value(item)
447+
elif isinstance(value, set):
448+
for item in value:
449+
self._validate_value(item)
450+
437451
def validate(self):
438452
"""Validate a value"""
439453
if not self._validated:
@@ -443,8 +457,7 @@ def validate(self):
443457
for k, argument in self.xpmtype.arguments.items():
444458
value = self.values.get(k)
445459
if value is not None:
446-
if isinstance(value, Config):
447-
value.__xpm__.validate()
460+
self._validate_value(value)
448461
elif argument.required:
449462
if not argument.generator:
450463
raise ValueError(

src/experimaestro/tests/test_validation.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,34 @@ def test_validation_taskargument():
206206
with TemporaryExperiment("fake"):
207207
x.submit(run_mode=RunMode.DRY_RUN)
208208
expect_validate(TaskConfigConsumer.C(x=x))
209+
210+
211+
# --- Nested structures (list, dict, set)
212+
213+
214+
class ListConfig(Config):
215+
items: Param[list[A]]
216+
217+
218+
class DictConfig(Config):
219+
items: Param[dict[str, A]]
220+
221+
222+
def test_validation_list_valid():
223+
"""Validate recurses into list elements"""
224+
expect_validate(ListConfig.C(items=[A.C(value=1), A.C(value=2)]))
225+
226+
227+
def test_validation_list_missing():
228+
"""Validate catches missing required params inside list elements"""
229+
expect_notvalidate(ListConfig.C(items=[A.C(value=1), A.C()]))
230+
231+
232+
def test_validation_dict_valid():
233+
"""Validate recurses into dict values"""
234+
expect_validate(DictConfig.C(items={"x": A.C(value=1), "y": A.C(value=2)}))
235+
236+
237+
def test_validation_dict_missing():
238+
"""Validate catches missing required params inside dict values"""
239+
expect_notvalidate(DictConfig.C(items={"x": A.C(value=1), "y": A.C()}))

0 commit comments

Comments
 (0)