Skip to content

Commit c80168d

Browse files
authored
Merge pull request #201 from GREENRAT-K405/fix/fragile-parameter-parsing
Fix fragile parameter parsing in concore.py and concoredocker.py
2 parents 0c1ed0d + 6877489 commit c80168d

3 files changed

Lines changed: 118 additions & 24 deletions

File tree

concore.py

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,26 +156,50 @@ def safe_literal_eval(filename, defaultValue):
156156
# ===================================================================
157157
# Parameter Parsing
158158
# ===================================================================
159+
def parse_params(sparams: str) -> dict:
160+
params = {}
161+
if not sparams:
162+
return params
163+
164+
s = sparams.strip()
165+
166+
#full dict literal
167+
if s.startswith("{") and s.endswith("}"):
168+
try:
169+
val = literal_eval(s)
170+
if isinstance(val, dict):
171+
return val
172+
except (ValueError, SyntaxError):
173+
pass
174+
175+
for item in s.split(";"):
176+
if "=" in item:
177+
key, value = item.split("=", 1) # split only once
178+
key=key.strip()
179+
value=value.strip()
180+
#try to convert to python type (int, float, list, etc.)
181+
# Use literal_eval to preserve backward compatibility (integers/lists)
182+
# Fallback to string for unquoted values (paths, URLs)
183+
try:
184+
params[key] = literal_eval(value)
185+
except (ValueError, SyntaxError):
186+
params[key] = value
187+
return params
188+
159189
try:
160190
sparams_path = concore_params_file
161191
if os.path.exists(sparams_path):
162192
with open(sparams_path, "r") as f:
163-
sparams = f.read()
193+
sparams = f.read().strip()
164194
if sparams: # Ensure sparams is not empty
165195
# Windows sometimes keeps quotes
166196
if sparams[0] == '"' and sparams[-1] == '"': #windows keeps "" need to remove
167197
sparams = sparams[1:-1]
168198

169-
# Convert key=value;key2=value2 to Python dict format
170-
if sparams != '{' and not (sparams.startswith('{') and sparams.endswith('}')): # Check if it needs conversion
171-
logging.debug("converting sparams: "+sparams)
172-
sparams = "{'"+re.sub(';',",'",re.sub('=',"':",re.sub(' ','',sparams)))+"}"
173-
logging.debug("converted sparams: " + sparams)
174-
try:
175-
params = literal_eval(sparams)
176-
except Exception as e:
177-
logging.warning(f"bad params content: {sparams}, error: {e}")
178-
params = dict()
199+
# Parse params using clean function instead of regex
200+
logging.debug("parsing sparams: "+sparams)
201+
params = parse_params(sparams)
202+
logging.debug("parsed params: " + str(params))
179203
else:
180204
params = dict()
181205
else:

concoredocker.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,53 @@ def safe_literal_eval(filename, defaultValue):
2525
concore_maxtime_file = os.path.join(inpath, "1", "concore.maxtime")
2626

2727
#9/21/22
28+
def parse_params(sparams):
29+
params = {}
30+
if not sparams:
31+
return params
32+
33+
s = sparams.strip()
34+
35+
# full dict literal
36+
if s.startswith("{") and s.endswith("}"):
37+
try:
38+
val = literal_eval(s)
39+
if isinstance(val, dict):
40+
return val
41+
except (ValueError, SyntaxError):
42+
pass
43+
44+
# keep backward compatibility: comma-separated params
45+
for item in s.split(","):
46+
if "=" in item:
47+
key, value = item.split("=", 1)
48+
key = key.strip()
49+
value = value.strip()
50+
#try to convert to python type (int, float, list, etc.)
51+
# Use literal_eval to preserve backward compatibility (integers/lists)
52+
# Fallback to string for unquoted values (paths, URLs)
53+
try:
54+
params[key] = literal_eval(value)
55+
except (ValueError, SyntaxError):
56+
params[key] = value
57+
return params
58+
2859
try:
29-
sparams = open(concore_params_file).read()
30-
if sparams[0] == '"': #windows keeps "" need to remove
60+
with open(concore_params_file, "r") as f:
61+
sparams = f.read().strip()
62+
63+
if sparams and sparams[0] == '"': # windows keeps quotes
3164
sparams = sparams[1:]
32-
sparams = sparams[0:sparams.find('"')]
33-
if sparams != '{':
34-
print("converting sparams: "+sparams)
35-
sparams = "{'"+re.sub(',',",'",re.sub('=',"':",re.sub(' ','',sparams)))+"}"
36-
print("converted sparams: " + sparams)
37-
try:
38-
params = literal_eval(sparams)
39-
except:
40-
print("bad params: "+sparams)
41-
except:
65+
if '"' in sparams:
66+
sparams = sparams[:sparams.find('"')]
67+
68+
if sparams:
69+
print("parsing sparams:", sparams)
70+
params = parse_params(sparams)
71+
else:
72+
params = dict()
73+
except Exception as e:
74+
print(f"Error reading concore.params: {e}")
4275
params = dict()
4376

4477
#9/30/22

tests/test_concore.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,41 @@ def test_core_functions_exist(self):
8484

8585
assert callable(safe_literal_eval)
8686
assert callable(tryparam)
87-
assert callable(default_maxtime)
87+
assert callable(default_maxtime)
88+
89+
90+
class TestParseParams:
91+
92+
def test_simple_key_value_pairs(self):
93+
from concore import parse_params
94+
params = parse_params("a=1;b=2")
95+
assert params == {"a": 1, "b": 2}
96+
97+
def test_preserves_whitespace_in_values(self):
98+
from concore import parse_params
99+
params = parse_params("label = hello world ; x = 5")
100+
assert params["label"] == "hello world"
101+
assert params["x"] == 5
102+
103+
def test_embedded_equals_in_value(self):
104+
from concore import parse_params
105+
params = parse_params("url=https://example.com?a=1&b=2")
106+
assert params["url"] == "https://example.com?a=1&b=2"
107+
108+
def test_numeric_and_list_coercion(self):
109+
from concore import parse_params
110+
params = parse_params("delay=5;coeffs=[1,2,3]")
111+
assert params["delay"] == 5
112+
assert params["coeffs"] == [1, 2, 3]
113+
114+
def test_dict_literal_backward_compatibility(self):
115+
from concore import parse_params
116+
params = parse_params("{'a': 1, 'b': 2}")
117+
assert params == {"a": 1, "b": 2}
118+
119+
def test_windows_quoted_input(self):
120+
from concore import parse_params
121+
s = "\"a=1;b=2\""
122+
s = s[1:-1] # simulate quote stripping before parse_params
123+
params = parse_params(s)
124+
assert params == {"a": 1, "b": 2}

0 commit comments

Comments
 (0)