Skip to content

Commit

Permalink
Merge pull request #831 from douglasjacobsen/variable-modification-un…
Browse files Browse the repository at this point in the history
…ique

Allow repeated variable modifications
  • Loading branch information
linsword13 authored Jan 16, 2025
2 parents 7865e8b + f7ee8c7 commit db3ff06
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 31 deletions.
15 changes: 10 additions & 5 deletions lib/ramble/ramble/language/modifier_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,16 @@ def _execute_variable_modification(mod):
if mode_name not in mod.variable_modifications:
mod.variable_modifications[mode_name] = {}

mod.variable_modifications[mode_name][name] = {
"modification": modification,
"method": method,
"separator": separator,
}
if name not in mod.variable_modifications[mode_name]:
mod.variable_modifications[mode_name][name] = []

mod.variable_modifications[mode_name][name].append(
{
"modification": modification,
"method": method,
"separator": separator,
}
)

return _execute_variable_modification

Expand Down
43 changes: 23 additions & 20 deletions lib/ramble/ramble/modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,26 +154,29 @@ def modded_variables(self, app, extra_vars={}):
if self._usage_mode not in self.variable_modifications:
return mods

for var, var_mod in self.variable_modifications[self._usage_mode].items():
if var_mod["method"] in ["append", "prepend"]:
if var in extra_vars:
prev_val = extra_vars[var]
elif var in app.variables:
prev_val = app.variables[var]
else:
prev_val = ""

if prev_val != "" and prev_val is not None:
sep = var_mod["separator"]
else:
sep = ""

if var_mod["method"] == "append":
mods[var] = f'{prev_val}{sep}{var_mod["modification"]}'
else: # method == prepend
mods[var] = f'{var_mod["modification"]}{sep}{prev_val}'
else: # method == set
mods[var] = var_mod["modification"]
for var, var_mods in self.variable_modifications[self._usage_mode].items():
for var_mod in var_mods:
if var_mod["method"] in ["append", "prepend"]:
if var in mods:
prev_val = mods[var]
elif var in extra_vars:
prev_val = extra_vars[var]
elif var in app.variables:
prev_val = app.variables[var]
else:
prev_val = ""

if prev_val != "" and prev_val is not None:
sep = var_mod["separator"]
else:
sep = ""

if var_mod["method"] == "append":
mods[var] = f'{prev_val}{sep}{var_mod["modification"]}'
else: # method == prepend
mods[var] = f'{var_mod["modification"]}{sep}{prev_val}'
else: # method == set
mods[var] = var_mod["modification"]

return mods

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2022-2025 The Ramble Authors
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

import os

from ramble.test.dry_run_helpers import dry_run_config, SCOPES
import ramble.test.modifier_functionality.modifier_helpers as modifier_helpers

import ramble.workspace
from ramble.main import RambleCommand

workspace = RambleCommand("workspace")


def test_repeated_variable_modifications(
mutable_mock_workspace_path, mutable_applications, mock_modifiers, request
):
workspace_name = request.node.name

test_modifiers = [
(SCOPES.experiment, modifier_helpers.named_modifier("repeat-var-mod")),
]

with ramble.workspace.create(workspace_name) as ws1:
ws1.write()

config_path = os.path.join(ws1.config_dir, ramble.workspace.config_file_name)

dry_run_config("modifiers", test_modifiers, config_path, "gromacs", "water_bare")

ws1._re_read()

workspace("concretize", global_args=["-D", ws1.root])
workspace("setup", "--dry-run", global_args=["-D", ws1.root])

rendered_template = os.path.join(
ws1.experiment_dir, "gromacs", "water_bare", "test_exp", "execute_experiment"
)
assert os.path.exists(rendered_template)

with open(rendered_template) as f:
data = f.read()
assert "prefix_mpi_command" in data
assert "suffix_mpi_command" in data
39 changes: 33 additions & 6 deletions lib/ramble/ramble/test/modifier_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,54 @@ def test_variable_modification_directive(mod_class):

mod_inst = mod_class("/not/a/path")
test_defs.append(add_variable_modification(mod_inst).copy())
test_defs.append(add_variable_modification(mod_inst, 2).copy())
test_defs.append(add_variable_modification(mod_inst).copy())

expected_attrs = ["modification", "method"]

assert hasattr(mod_inst, "variable_modifications")

mod_count = 0
for mode_name in mod_inst.variable_modifications:
for var_name in mod_inst.variable_modifications[mode_name]:
for modification in mod_inst.variable_modifications[mode_name][var_name]:
mod_count += 1
assert mod_count == 9 # Each call to add_variable_modification adds 3 modifications

for test_def in test_defs:
var_name = test_def["name"]
mode_name = test_def["mode"]

assert mode_name in mod_inst.variable_modifications
assert var_name in mod_inst.variable_modifications[mode_name]
for attr in expected_attrs:
assert attr in mod_inst.variable_modifications[mode_name][var_name]
assert test_def[attr] == mod_inst.variable_modifications[mode_name][var_name][attr]
found_match = (
False if len(mod_inst.variable_modifications[mode_name][var_name]) > 0 else True
)
for modification in mod_inst.variable_modifications[mode_name][var_name]:
match = True
for attr in expected_attrs:
assert attr in modification
if test_def[attr] != modification[attr]:
match = False
if match:
found_match = True
assert found_match

for mode_name in test_def["modes"]:
found_match = (
False if len(mod_inst.variable_modifications[mode_name][var_name]) > 0 else True
)
assert mode_name in mod_inst.variable_modifications
assert var_name in mod_inst.variable_modifications[mode_name]
for attr in expected_attrs:
assert attr in mod_inst.variable_modifications[mode_name][var_name]
assert test_def[attr] == mod_inst.variable_modifications[mode_name][var_name][attr]
for modification in mod_inst.variable_modifications[mode_name][var_name]:
match = True
for attr in expected_attrs:
assert attr in modification
if test_def[attr] != modification[attr]:
match = False
if match:
found_match = True
assert found_match


@pytest.mark.parametrize("mod_class", mod_types)
Expand Down
34 changes: 34 additions & 0 deletions var/ramble/repos/builtin.mock/modifiers/repeat-var-mod/modifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2022-2025 The Ramble Authors
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

from ramble.modkit import * # noqa: F403


class RepeatVarMod(BasicModifier):
"""Define a test modifier with repeat variable modifications"""

name = "repeat-var-mod"

tags("test")

mode("test", description="This is a test mode")
default_mode("test")

variable_modification(
"mpi_command",
'echo "prefix_mpi_command" >> {log_file};',
method="prepend",
modes=["test"],
)

variable_modification(
"mpi_command",
'echo "suffix_mpi_command" >> {log_file};',
method="append",
modes=["test"],
)

0 comments on commit db3ff06

Please sign in to comment.