diff --git a/src/openai/_qs.py b/src/openai/_qs.py index 4127c19c62..d3c34ce6b0 100644 --- a/src/openai/_qs.py +++ b/src/openai/_qs.py @@ -112,10 +112,9 @@ def _stringify_item( f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" ) - serialised = self._primitive_value_to_str(value) - if not serialised: + if value is None: return [] - return [(key, serialised)] + return [(key, self._primitive_value_to_str(value))] def _primitive_value_to_str(self, value: PrimitiveData) -> str: # copied from httpx diff --git a/tests/test_qs.py b/tests/test_qs.py index 697b8a95ec..dc5fd979c6 100644 --- a/tests/test_qs.py +++ b/tests/test_qs.py @@ -22,6 +22,14 @@ def test_basic() -> None: assert stringify({"a": None}) == "" +def test_empty_string_scalar_is_kept_distinct_from_none() -> None: + # An explicit empty string is a real value ("a=") and must not be + # conflated with omitting the key entirely (None). + assert unquote(stringify({"a": ""})) == "a=" + assert stringify({"a": None}) == "" + assert unquote(stringify({"a": "", "b": 1})) == "a=&b=1" + + @pytest.mark.parametrize("method", ["class", "function"]) def test_nested_dotted(method: str) -> None: if method == "class":