fix(tools): coerce an empty-string list/dict argument to an empty container (#1024)

This commit is contained in:
Ahmed Allam 2026-08-09 02:58:30 +03:00 committed by GitHub
parent 72833b8e43
commit c29eb73c7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 2 deletions

View file

@ -160,7 +160,9 @@ def _schema_types(spec: dict[str, Any]) -> set[str]:
def _decode_structured(value: str, types: set[str]) -> Any: def _decode_structured(value: str, types: set[str]) -> Any:
stripped = value.strip() stripped = value.strip()
if not stripped: if not stripped:
return value # An empty string is the model's "no value" for a list/dict param; give it
# the empty container so it validates instead of failing the type check.
return [] if "array" in types else {}
try: try:
decoded = json.loads(stripped) decoded = json.loads(stripped)
except json.JSONDecodeError: except json.JSONDecodeError:

View file

@ -70,7 +70,6 @@ async def test_encoded_list_is_decoded_for_an_array_parameter(schema: dict[str,
"auth", "auth",
"Endpoint /admin leaks user data, and session tokens never expire", "Endpoint /admin leaks user data, and session tokens never expire",
'"auth"', '"auth"',
"",
], ],
) )
async def test_free_form_strings_are_never_split_into_an_array(value: str) -> None: async def test_free_form_strings_are_never_split_into_an_array(value: str) -> None:
@ -79,6 +78,29 @@ async def test_free_form_strings_are_never_split_into_an_array(value: str) -> No
assert parsed["tags"] == value assert parsed["tags"] == value
@pytest.mark.asyncio
@pytest.mark.parametrize("schema", [_ARRAY, _NULLABLE_ARRAY])
@pytest.mark.parametrize("value", ["", " "])
async def test_empty_string_becomes_an_empty_array(schema: dict[str, Any], value: str) -> None:
parsed = await _roundtrip(schema, {"tags": value})
assert parsed["tags"] == []
@pytest.mark.asyncio
async def test_empty_string_becomes_an_empty_object() -> None:
parsed = await _roundtrip(_OBJECT, {"modifications": ""})
assert parsed["modifications"] == {}
@pytest.mark.asyncio
async def test_empty_string_for_a_string_parameter_is_untouched() -> None:
parsed = await _roundtrip(_STRING, {"todos": ""})
assert parsed["todos"] == ""
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_encoded_mapping_is_decoded_for_an_object_parameter() -> None: async def test_encoded_mapping_is_decoded_for_an_object_parameter() -> None:
parsed = await _roundtrip(_OBJECT, {"modifications": '{"method": "POST"}'}) parsed = await _roundtrip(_OBJECT, {"modifications": '{"method": "POST"}'})