mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 12:34:17 +02:00
Key query expansion off the template, not the value type
🤖 Generated with Claude Code Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAKNJthovhc5VSi4MTiZUe
This commit is contained in:
parent
f790c31c82
commit
d709bb1aba
2 changed files with 24 additions and 4 deletions
|
|
@ -197,8 +197,12 @@ def expand_uri_template(uri_template: str, params: dict[str, Any]) -> str:
|
|||
names = [n.strip() for n in match.group(1).split(",")]
|
||||
parts = []
|
||||
for name in names:
|
||||
# `{?tags*}` is the explode form; the value is a list, emitted as a
|
||||
# repeated key so it round-trips back through match_uri_template.
|
||||
# The template decides the serialization, not the runtime value:
|
||||
# `{?tags*}` emits a repeated key, `{?tags}` stays a single value.
|
||||
# Keying off the value type instead would expand a list under a
|
||||
# plain `{?tags}`, which match_uri_template then reads back as just
|
||||
# its first element.
|
||||
exploded = name.endswith("*")
|
||||
name = name.removesuffix("*")
|
||||
underscored = name.replace("-", "_")
|
||||
if name in params:
|
||||
|
|
@ -207,8 +211,10 @@ def expand_uri_template(uri_template: str, params: dict[str, Any]) -> str:
|
|||
value = params[underscored]
|
||||
else:
|
||||
continue
|
||||
values = value if isinstance(value, (list, tuple)) else [value]
|
||||
parts.extend(f"{quote(name)}={quote(str(v))}" for v in values)
|
||||
if exploded and isinstance(value, (list, tuple)):
|
||||
parts.extend(f"{quote(name)}={quote(str(v))}" for v in value)
|
||||
else:
|
||||
parts.append(f"{quote(name)}={quote(str(value))}")
|
||||
if parts:
|
||||
return "?" + "&".join(parts)
|
||||
return ""
|
||||
|
|
|
|||
|
|
@ -1119,6 +1119,20 @@ class TestMatchExpandRoundTrip:
|
|||
assert params is not None
|
||||
assert expand_uri_template(template, params) == uri
|
||||
|
||||
def test_plain_query_param_does_not_repeat_a_sequence_value(self):
|
||||
"""The template decides repeatability, not the runtime value's type.
|
||||
|
||||
A list handed to a plain `{?tags}` must stay one value: expanding it as
|
||||
a repeated key produced a URI that matched back to only its first
|
||||
element, contradicting the scalar contract `{?tags}` documents.
|
||||
"""
|
||||
uri = expand_uri_template("test://x{?tags}", {"tags": ["a", "b"]})
|
||||
assert uri.count("tags=") == 1
|
||||
|
||||
params = match_uri_template(uri, "test://x{?tags}")
|
||||
assert params is not None
|
||||
assert expand_uri_template("test://x{?tags}", params) == uri
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template, params",
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue