Skip to content

Commit d6d025a

Browse files
dralleypedro-psb
authored andcommitted
Allow commas in pulp_labels values
The label validator now permits commas in values, and the LabelFilter splits only on unescaped commas so that backslash-escaped commas in filter queries (e.g. ?pulp_label_select=key=val\,ue) match literal comma values. closes #7789 Assisted-By: Claude Opus 4.6
1 parent aa18b29 commit d6d025a

5 files changed

Lines changed: 58 additions & 5 deletions

File tree

CHANGES/7789.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Allow commas in `pulp_labels` values. To filter for labels containing commas, escape them
2+
with a backslash (e.g. `?pulp_label_select=key=val\,ue`).

pulp_file/tests/functional/api/test_labels.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,58 @@ def test_invalid_labels(file_repository_factory):
118118
assert e_info.value.status == 400
119119

120120
with pytest.raises(ApiException) as e_info:
121-
file_repository_factory(name=str(uuid4()), pulp_labels={"arda": "eru,illuvata"})
121+
file_repository_factory(name=str(uuid4()), pulp_labels={"arda": "eru(illuvata)"})
122122
assert e_info.value.status == 400
123123

124124

125+
@pytest.mark.parallel
126+
def test_labels_with_commas(file_repository_factory, file_bindings, monitor_task):
127+
"""Test that label values can contain commas."""
128+
labels = {"signed_by": "release4,release2"}
129+
file_repo = file_repository_factory(name=str(uuid4()), pulp_labels=labels)
130+
assert file_repo.pulp_labels == labels
131+
132+
labels["signed_by"] = "a,b,c"
133+
monitor_task(
134+
file_bindings.RepositoriesFileApi.partial_update(
135+
file_repo.pulp_href, {"pulp_labels": labels}
136+
).task
137+
)
138+
file_repo = file_bindings.RepositoriesFileApi.read(file_repo.pulp_href)
139+
assert file_repo.pulp_labels == labels
140+
141+
142+
@pytest.mark.parallel
143+
def test_label_select_with_commas(file_repository_factory, file_bindings):
144+
"""Test filtering labels whose values contain commas using backslash-escaped commas."""
145+
key = str(uuid4()).replace("-", "")
146+
147+
file_repository_factory(name=str(uuid4()), pulp_labels={key: "release4,release2"})
148+
file_repository_factory(name=str(uuid4()), pulp_labels={key: "release4"})
149+
150+
# Escaped comma in value matches the label with a comma
151+
results = file_bindings.RepositoriesFileApi.list(
152+
pulp_label_select=f"{key}=release4\\,release2"
153+
).results
154+
assert len(results) == 1
155+
assert results[0].pulp_labels[key] == "release4,release2"
156+
157+
# Unescaped comma is still treated as a filter term separator
158+
results = file_bindings.RepositoriesFileApi.list(pulp_label_select=f"{key}=release4").results
159+
assert len(results) == 1
160+
assert results[0].pulp_labels[key] == "release4"
161+
162+
# Escaped comma value combined with a second filter term
163+
key2 = str(uuid4()).replace("-", "")
164+
file_repository_factory(name=str(uuid4()), pulp_labels={key: "release4,release2", key2: "true"})
165+
results = file_bindings.RepositoriesFileApi.list(
166+
pulp_label_select=f"{key}=release4\\,release2,{key2}=true"
167+
).results
168+
assert len(results) == 1
169+
assert results[0].pulp_labels[key] == "release4,release2"
170+
assert results[0].pulp_labels[key2] == "true"
171+
172+
125173
# Label Filtering
126174

127175

pulpcore/app/serializers/fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,9 @@ def pulp_labels_validator(value):
435435
" spaces, hyphens, and dots are allowed."
436436
).format(k)
437437
)
438-
if v is not None and re.search(r"[,()]", v):
438+
if v is not None and re.search(r"[()]", v):
439439
raise serializers.ValidationError(
440-
_("Key '{}' contains value with comma or parenthesis.").format(k)
440+
_("Key '{}' contains value with parenthesis.").format(k)
441441
)
442442

443443
return value

pulpcore/app/viewsets/custom_filters.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,13 @@ def filter(self, qs, value):
311311
# user didn't supply a value
312312
return qs
313313

314-
for term in value.split(","):
314+
for term in re.split(r"(?<!\\),", value):
315315
match = re.match(rf"(!?{LABEL_KEY_CHARS}+)(=|!=|~)?(.*)?", term)
316316
if not match:
317317
raise DRFValidationError(_("Invalid search term: '{}'.").format(term))
318318
key, op, val = match.groups()
319+
if val is not None:
320+
val = val.replace("\\,", ",")
319321

320322
if key.startswith("!") and op:
321323
raise DRFValidationError(_("Cannot use an operator with '{}'.").format(key))

pulpcore/tests/unit/serializers/test_fields.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
pytest.param({"my key": "value"}, id="spaced-key"),
2121
pytest.param({"my-dotted.key": "value"}, id="dotted-dash-key"),
2222
pytest.param({"spaced key-with.mixed_chars": "value"}, id="all-key"),
23+
pytest.param({"key": "val,ue"}, id="comma-value"),
24+
pytest.param({"key": "a,b,c"}, id="multiple-commas-value"),
2325
],
2426
)
2527
def test_pulp_labels_validator_valid(labels):
@@ -31,7 +33,6 @@ def test_pulp_labels_validator_valid(labels):
3133
@pytest.mark.parametrize(
3234
"labels",
3335
[
34-
pytest.param({"key": "val,ue"}, id="comma-value"),
3536
pytest.param({"key": "val(ue"}, id="open-parenthesis-value"),
3637
pytest.param({"key": "val)ue"}, id="close-parenthesis-value"),
3738
pytest.param({"bad!key": "value"}, id="exclamation-key"),

0 commit comments

Comments
 (0)