Skip to content

Commit a05399c

Browse files
authored
fix: Fix config set (#207)
# Pull Request Title ## Description The `firewheel config set -s ...` command does not properly handle whitespace. It takes arguments from the command line and splits them using whitespace (ignoring CLI indicators like quoted parameters). Instead of using the `str.split` method, the `ConfigureFirewheel` object (and the underlying `Config` object) should use `shlex.split`. ## Related Issue See [related discussion](#166 (review)) on #166. ## Type of Change Please select the type of change your pull request introduces: - [x] Bugfix - [ ] New feature - [ ] Documentation update - [ ] Other (please describe): ## Checklist - [x] This PR conforms to the process detailed in the [Contributing Guide](https://sandialabs.github.io/firewheel/developer/contributing.html). - [x] I have included no proprietary/sensitive information in my code. - [x] I have performed a self-review of my code. - [ ] N/A ~I have commented my code, particularly in hard-to-understand areas.~ - [ ] N/A ~I have made corresponding changes to the documentation.~ - [x] My changes generate no new warnings. - [x] I have tested my code.
1 parent 7591256 commit a05399c

File tree

5 files changed

+59
-8
lines changed

5 files changed

+59
-8
lines changed

src/firewheel/cli/configure_firewheel.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import cmd
3+
import shlex
34
import pprint
45
import argparse
56
import operator
@@ -43,7 +44,7 @@ def _handle_parsing(
4344
# Print the full help message on an error
4445
# (see: https://stackoverflow.com/a/29293080)
4546
try:
46-
cmd_args = parser.parse_args(args.split())
47+
cmd_args = parser.parse_args(shlex.split(args))
4748
except SystemExit as err:
4849
if err.code == 2:
4950
parser.print_help()
@@ -202,7 +203,7 @@ def do_set(self, args: str) -> None: # noqa: DOC502
202203

203204
if cmd_args.single is not None:
204205
key = cmd_args.single[0]
205-
value = " ".join(cmd_args.single[1:])
206+
value = shlex.join(cmd_args.single[1:])
206207
self.log.debug(
207208
"Setting the FIREWHEEL config value for `%s` to `%s`.", key, value
208209
)

src/firewheel/cli/executors/shell.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,16 @@ def execute(
6565
"MM_FORCE",
6666
"MM_RECOVER",
6767
"MM_CGROUP",
68-
"MM_APPEND"
68+
"MM_APPEND",
6969
}
7070

7171
# Concatenate minimega environment variables
7272
env_vars = [
73-
*(f"{env}={os.environ[env]}" for env in minimega_vars if env in os.environ),
73+
*(
74+
f"{env}={os.environ[env]}"
75+
for env in minimega_vars
76+
if env in os.environ
77+
),
7478
f"FIREWHEEL={fw_path}",
7579
f"FIREWHEEL_PYTHON={sys.executable}",
7680
f"FIREWHEEL_GRPC_SERVER={grpc_path}",

src/firewheel/config/_config.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
firewheel.log
2121
"""
2222

23+
import shlex
2324
import shutil
2425
from typing import Any, Set, Dict, List, Final, Tuple, Union
2526
from pathlib import Path
@@ -315,8 +316,8 @@ def resolve_get(
315316
This helper method enables getting the value for a specific configuration
316317
key. If a nested key is requested it should be represented using periods
317318
to indicate the nesting. This function will return the Python object
318-
of the key. Alternatively, if the value if a list, the user can return
319-
a space separated string.
319+
of the key. Alternatively, if the value is a list, the user can return
320+
a space separated string.
320321
321322
Args:
322323
key (str): The input key to get in *dot* notation. This means that
@@ -389,7 +390,7 @@ def resolve_set(
389390
# Set the correct type of the input data
390391
try:
391392
if isinstance(cur_value, list):
392-
value = value.split()
393+
value = shlex.split(value)
393394
elif isinstance(cur_value, bool):
394395
value = bool(strtobool(value))
395396
elif cur_value is not None:

src/firewheel/tests/unit/cli/test_cli_configure.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def test_do_reset(self):
4545
new_config = Config().get_config()
4646
self.assertEqual(new_config["logging"]["cli_log"], default_setting)
4747

48-
def test_do_set_single(self):
48+
def test_do_set_single_string(self):
4949
new_log_name = "new_cli.log"
5050
old_setting = self.old_config["logging"]["cli_log"]
5151
self.assertNotEqual(old_setting, new_log_name)
@@ -56,6 +56,50 @@ def test_do_set_single(self):
5656

5757
self.assertEqual(new_config["logging"]["cli_log"], new_log_name)
5858

59+
def test_do_set_single_list_one_element(self):
60+
new_nodes_string = "test_node"
61+
old_setting = self.old_config["cluster"]["compute"]
62+
self.assertNotEqual(old_setting, new_nodes_string)
63+
args = f"-s cluster.compute {new_nodes_string}"
64+
self.cli.do_set(args)
65+
66+
new_config = Config().get_config()
67+
68+
self.assertEqual(new_config["cluster"]["compute"], [new_nodes_string])
69+
70+
def test_do_set_single_list_one_element_with_space(self):
71+
new_nodes_string = "test node"
72+
old_setting = self.old_config["cluster"]["compute"]
73+
self.assertNotEqual(old_setting, new_nodes_string)
74+
args = f"-s cluster.compute '{new_nodes_string}'"
75+
self.cli.do_set(args)
76+
77+
new_config = Config().get_config()
78+
79+
self.assertEqual(new_config["cluster"]["compute"], [new_nodes_string])
80+
81+
def test_do_set_single_list_multiple_elements(self):
82+
new_nodes_string = "test_node0,test_node1"
83+
old_setting = self.old_config["cluster"]["compute"]
84+
self.assertNotEqual(old_setting, new_nodes_string)
85+
args = f"-s cluster.compute {new_nodes_string}"
86+
self.cli.do_set(args)
87+
88+
new_config = Config().get_config()
89+
90+
self.assertEqual(new_config["cluster"]["compute"], [new_nodes_string])
91+
92+
def test_do_set_single_list_multiple_elements_space(self):
93+
new_nodes_string = "test_node0 test_node1"
94+
old_setting = self.old_config["cluster"]["compute"]
95+
self.assertNotEqual(old_setting, new_nodes_string)
96+
args = f"-s cluster.compute {new_nodes_string}"
97+
self.cli.do_set(args)
98+
99+
new_config = Config().get_config()
100+
101+
self.assertEqual(new_config["cluster"]["compute"], new_nodes_string.split(" "))
102+
59103
@unittest.mock.patch("sys.stderr", new_callable=io.StringIO)
60104
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
61105
def test_do_set_incorrect(self, mock_stdout, mock_stderr):

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ commands = coverage erase
2727
basepython = python3
2828
extras = format
2929
commands =
30+
ruff check --select I --fix {toxinidir}/src/firewheel
3031
ruff format {toxinidir}/src/firewheel
3132

3233
# Linters

0 commit comments

Comments
 (0)