Skip to content

Commit d8610ed

Browse files
authored
Merge pull request #951 from douglasjacobsen/concretize-permits-errors
Permit errors when concretizing workspace
2 parents f8fc9b1 + 8f4f10a commit d8610ed

File tree

9 files changed

+100
-34
lines changed

9 files changed

+100
-34
lines changed

lib/ramble/ramble/application.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,14 @@ def build_modifier_instances(self):
903903
self.expander.add_no_expand_var(var)
904904
mod_inst.expander.add_no_expand_var(var)
905905

906-
def validate_experiment(self):
906+
def validate_experiment(self, warn_validation=True, die_on_validate_error=True):
907907
# Validate the new modifiers variables exist
908908
# (note: the base ramble variables are checked earlier too)
909-
self.keywords.check_required_keys(self.variables)
909+
self.keywords.check_required_keys(
910+
self.variables,
911+
warn_validation=warn_validation,
912+
die_on_validate_error=die_on_validate_error,
913+
)
910914
self._check_object_validators()
911915

912916
def _check_object_validators(self):

lib/ramble/ramble/experiment_set.py

+33-15
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def set_workload_context(self, workload_context):
138138

139139
self._set_context(self._contexts.workload, workload_context)
140140

141-
def set_experiment_context(self, experiment_context):
141+
def set_experiment_context(self, experiment_context, die_on_validate_error=True):
142142
"""Set up current experiment context"""
143143

144144
try:
@@ -148,7 +148,7 @@ def set_experiment_context(self, experiment_context):
148148
raise RambleVariableDefinitionError(f"In experiment {namespace}: {e}")
149149

150150
self._set_context(self._contexts.experiment, experiment_context)
151-
self._ingest_experiments()
151+
self._ingest_experiments(die_on_validate_error=die_on_validate_error)
152152

153153
@property
154154
def application_namespace(self):
@@ -253,7 +253,15 @@ def _compute_mpi_vars(self, expander, variables):
253253
if not n_threads:
254254
variables[self.keywords.n_threads] = 1
255255

256-
def _prepare_experiment(self, exp_template_name, variables, context, repeats):
256+
def _prepare_experiment(
257+
self,
258+
exp_template_name,
259+
variables,
260+
context,
261+
repeats,
262+
die_on_validate_error=True,
263+
warn_validation=True,
264+
):
257265
"""Prepare an experiment instance
258266
259267
Create an experiment instance based on the input variables, context,
@@ -268,6 +276,7 @@ def _prepare_experiment(self, exp_template_name, variables, context, repeats):
268276
Returns:
269277
(Application): Instance of an application class for this experiment
270278
"""
279+
271280
experiment_suffix = ""
272281
# After generating the base experiment, append the index to repeat experiments
273282
if repeats.repeat_index:
@@ -348,14 +357,9 @@ def _prepare_experiment(self, exp_template_name, variables, context, repeats):
348357
app_inst.add_expand_vars(self._workspace)
349358
app_inst.read_status()
350359

351-
try:
352-
app_inst.validate_experiment()
353-
except ramble.keywords.RambleKeywordError as e:
354-
raise RambleVariableDefinitionError(str(e))
355-
356360
return app_inst
357361

358-
def _ingest_experiments(self):
362+
def _ingest_experiments(self, die_on_validate_error=True):
359363
"""Ingest experiments based on the current context.
360364
361365
Merge all contexts, and render individual experiments. Track these
@@ -463,7 +467,12 @@ def _ingest_experiments(self):
463467
tracking_group, exclude_where=exclude_where, ignore_used=False, fatal=False
464468
):
465469
app_inst = self._prepare_experiment(
466-
experiment_template_name, tracking_vars, final_context, repeats
470+
experiment_template_name,
471+
tracking_vars,
472+
final_context,
473+
repeats,
474+
warn_validation=False,
475+
die_on_validate_error=die_on_validate_error,
467476
)
468477

469478
final_exp_name = app_inst.expander.expand_var_name(self.keywords.experiment_namespace)
@@ -477,7 +486,12 @@ def _ingest_experiments(self):
477486
render_group, exclude_where=exclude_where
478487
):
479488
app_inst = self._prepare_experiment(
480-
experiment_template_name, experiment_vars, final_context, repeats
489+
experiment_template_name,
490+
experiment_vars,
491+
final_context,
492+
repeats,
493+
warn_validation=False,
494+
die_on_validate_error=die_on_validate_error,
481495
)
482496

483497
final_exp_name = app_inst.expander.expand_var_name(self.keywords.experiment_name)
@@ -526,11 +540,15 @@ def _ingest_experiments(self):
526540
logger.die(f"Experiment {final_exp_namespace} is not unique.")
527541

528542
try:
529-
self.keywords.check_required_keys(experiment_vars)
530-
except ramble.keywords.RambleKeywordError as e:
531-
raise RambleVariableDefinitionError(
532-
f"In experiment {final_exp_namespace}: {e}"
543+
app_inst.validate_experiment(
544+
warn_validation=True, die_on_validate_error=die_on_validate_error
533545
)
546+
except ramble.keywords.RambleKeywordError as e:
547+
if die_on_validate_error:
548+
raise RambleVariableDefinitionError(
549+
f"In experiment {final_exp_namespace}: {e}"
550+
)
551+
pass
534552

535553
rendered_experiments.add(final_exp_namespace)
536554
self.experiments[final_exp_namespace] = app_inst

lib/ramble/ramble/keywords.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def check_reserved_keys(self, definitions):
168168
f'Keyword "{definition}" has been defined, ' + "but is reserved by ramble."
169169
)
170170

171-
def check_required_keys(self, definitions):
171+
def check_required_keys(self, definitions, warn_validation=True, die_on_validate_error=True):
172172
"""Check a dictionary of variable definitions for all required keywords"""
173173
if not definitions:
174174
return
@@ -183,11 +183,14 @@ def check_required_keys(self, definitions):
183183
required_set.remove(definition)
184184

185185
if len(required_set) > 0:
186-
for key in required_set:
187-
logger.warn(f'Required key "{key}" is not defined')
188-
raise RambleKeywordError(
189-
"One or more required keys " + "are not defined within an experiment."
190-
)
186+
if warn_validation:
187+
for key in required_set:
188+
logger.warn(f'Required key "{key}" is not defined')
189+
190+
if die_on_validate_error:
191+
raise RambleKeywordError(
192+
"One or more required keys " + "are not defined within an experiment."
193+
)
191194

192195

193196
class RambleKeywordError(ramble.error.RambleError):

lib/ramble/ramble/modkit.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
# except according to those terms.
88

99
# flake8: noqa: F401
10-
"""modkit is a set of useful modules to import when writing modifiers
11-
"""
10+
"""modkit is a set of useful modules to import when writing modifiers"""
1211

1312
import llnl.util.filesystem
1413
from llnl.util.filesystem import *

lib/ramble/ramble/pkgmankit.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
# except according to those terms.
88

99
# flake8: noqa: F401
10-
"""pkgmankit is a set of useful modules to import when writing package managers
11-
"""
10+
"""pkgmankit is a set of useful modules to import when writing package managers"""
1211

1312
import os
1413

lib/ramble/ramble/test/concretize_builtin.py

+43
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# everything here uses the mock_workspace_path
1717
pytestmark = pytest.mark.usefixtures("mutable_config", "mutable_mock_workspace_path")
1818

19+
config = RambleCommand("config")
1920
workspace = RambleCommand("workspace")
2021

2122

@@ -97,3 +98,45 @@ def test_concretize_does_not_set_required(mutable_config, mutable_mock_workspace
9798
req_test = False
9899
break
99100
assert req_test
101+
102+
103+
def test_concretize_allows_invalid_experiment(
104+
mutable_config, mutable_mock_workspace_path, request
105+
):
106+
ws_name = request.node.name
107+
108+
global_args = ["-w", ws_name]
109+
110+
with ramble.workspace.create(ws_name) as ws:
111+
112+
workspace(
113+
"manage",
114+
"experiments",
115+
"gromacs",
116+
"--wf",
117+
"water_bare",
118+
"-p",
119+
"spack",
120+
global_args=global_args,
121+
)
122+
123+
# Remove variables, to create an invalid experiment
124+
config(
125+
"rm",
126+
"applications:gromacs:workloads:water_bare:experiments:generated:n_ranks",
127+
global_args=global_args,
128+
)
129+
config(
130+
"rm",
131+
"applications:gromacs:workloads:water_bare:experiments:generated:n_nodes",
132+
global_args=global_args,
133+
)
134+
135+
ws._re_read()
136+
137+
workspace("concretize")
138+
139+
with open(ws.config_file_path) as f:
140+
data = f.read()
141+
142+
assert "spack_gromacs" in data

lib/ramble/ramble/wmkit.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
# except according to those terms.
88

99
# flake8: noqa: F401
10-
"""wmkit is a set of useful modules to import when writing workflow managers
11-
"""
10+
"""wmkit is a set of useful modules to import when writing workflow managers"""
1211

1312
from ramble.language.shared_language import *
1413
from ramble.language.workflow_manager_language import *

lib/ramble/ramble/workspace/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
# option. This file may not be copied, modified, or distributed
77
# except according to those terms.
88
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
9-
"""This package implements Ramble workspaces.
10-
"""
9+
"""This package implements Ramble workspaces."""
1110

1211
from .workspace import (
1312
RambleActiveWorkspaceError,

lib/ramble/ramble/workspace/workspace.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ def all_specs(self):
844844
def all_experiments_path(self):
845845
return os.path.join(self.root, workspace_all_experiments_file)
846846

847-
def build_experiment_set(self):
847+
def build_experiment_set(self, die_on_validate_error=True):
848848
"""Create an experiment set representing this workspace"""
849849

850850
experiment_set = ramble.experiment_set.ExperimentSet(self)
@@ -858,7 +858,9 @@ def build_experiment_set(self):
858858
experiment_set.set_workload_context(workload_context)
859859

860860
for _, experiment_context in self.all_experiments(experiments):
861-
experiment_set.set_experiment_context(experiment_context)
861+
experiment_set.set_experiment_context(
862+
experiment_context, die_on_validate_error=die_on_validate_error
863+
)
862864

863865
experiment_set.build_experiment_chains()
864866

@@ -1395,7 +1397,7 @@ def concretize(self, force=False, quiet=False):
13951397

13961398
self.software_environments = ramble.software_environments.SoftwareEnvironments(self)
13971399

1398-
experiment_set = self.build_experiment_set()
1400+
experiment_set = self.build_experiment_set(die_on_validate_error=False)
13991401

14001402
for _, app_inst, _ in experiment_set.all_experiments():
14011403
app_inst.build_modifier_instances()

0 commit comments

Comments
 (0)