Skip to content

Commit b58e462

Browse files
authored
Fix for brace-trimming regression (#108)
* adds tests for tokenized paths * only strip a trailing } when it’s truly unmatched
1 parent 887b00a commit b58e462

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

lib/envstack/util.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,13 +355,15 @@ def evaluate_modifiers(
355355
resolving = set()
356356

357357
def sanitize_value(value):
358-
# sanitize the value to ensure it is a string or a path
359358
if (
360359
isinstance(value, str)
361360
and value.endswith("}")
362361
and not value.startswith("${")
363362
):
364-
return value.rstrip("}")
363+
# trim a dangling '}' when there's no matching '{'
364+
if value.count("{") == 0 and value.count("}") == 1:
365+
return value.rstrip("}")
366+
return value
365367
elif isinstance(value, str) and detect_path(value):
366368
return dedupe_paths(value)
367369
return value

tests/test_util.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,61 @@ def test_pathlike_value_colon_separated(self):
124124
result = evaluate_modifiers(expression, environ)
125125
self.assertEqual(result, f"/usr/local/lib/env{os.pathsep}/mnt/env")
126126

127+
def test_tokenized_value_single(self):
128+
"""Test tokenized value with a single token."""
129+
expression = "${ROOT}/{foo}"
130+
environ = {
131+
"ROOT": "/var/tmp",
132+
}
133+
result = evaluate_modifiers(expression, environ)
134+
self.assertEqual(
135+
result, r"/var/tmp/{foo}"
136+
)
137+
138+
def test_tokenized_value_trailing_slash(self):
139+
"""Test tokenized value with a single token and trailing slash."""
140+
expression = "${ROOT}/{foo}/"
141+
environ = {
142+
"ROOT": "/var/tmp",
143+
}
144+
result = evaluate_modifiers(expression, environ)
145+
self.assertEqual(
146+
result, r"/var/tmp/{foo}/"
147+
)
148+
149+
def test_tokenized_value_two_tokens(self):
150+
"""Test tokenized value with two tokens."""
151+
expression = "${ROOT}/{foo}/{bar}"
152+
environ = {
153+
"ROOT": "/var/tmp",
154+
}
155+
result = evaluate_modifiers(expression, environ)
156+
self.assertEqual(
157+
result, r"/var/tmp/{foo}/{bar}"
158+
)
159+
160+
def test_tokenized_value_three_tokens(self):
161+
"""Test tokenized value with three tokens."""
162+
expression = "${ROOT}/{foo}/{bar}/{baz}"
163+
environ = {
164+
"ROOT": "/var/tmp",
165+
}
166+
result = evaluate_modifiers(expression, environ)
167+
self.assertEqual(
168+
result, r"/var/tmp/{foo}/{bar}/{baz}"
169+
)
170+
171+
def test_tokenized_value_three_tokens_slash(self):
172+
"""Test tokenized value with three tokens and a trailing slash."""
173+
expression = "${ROOT}/{foo}/{bar}/{baz}/"
174+
environ = {
175+
"ROOT": "/var/tmp",
176+
}
177+
result = evaluate_modifiers(expression, environ)
178+
self.assertEqual(
179+
result, r"/var/tmp/{foo}/{bar}/{baz}/"
180+
)
181+
127182
def test_default_value_with_default_args(self):
128183
"""Test default value with default args."""
129184
expression = "${HELLO:=world}"

0 commit comments

Comments
 (0)