From a8dd079be7028b2bf4cdbbbc3a6793a5f78ecc85 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 11 Dec 2022 19:12:08 +0000 Subject: [PATCH] Raise TypeError if content is passed a 'dict' instance. (#2495) --- httpx/_content.py | 6 +++++- tests/test_content.py | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/httpx/_content.py b/httpx/_content.py index 3cbca7ac69..1c450b7595 100644 --- a/httpx/_content.py +++ b/httpx/_content.py @@ -114,7 +114,11 @@ def encode_content( headers = {"Content-Length": str(content_length)} if body else {} return headers, ByteStream(body) - elif isinstance(content, Iterable): + elif isinstance(content, Iterable) and not isinstance(content, dict): + # `not isinstance(content, dict)` is a bit oddly specific, but it + # catches a case that's easy for users to make in error, and would + # otherwise pass through here, like any other bytes-iterable, + # because `dict` happens to be iterable. See issue #2491. content_length_or_none = peek_filelike_length(content) if content_length_or_none is None: diff --git a/tests/test_content.py b/tests/test_content.py index d9b4ed8e74..6a1303cc80 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -360,6 +360,9 @@ def test_invalid_argument(): with pytest.raises(TypeError): httpx.Request(method, url, content=123) # type: ignore + with pytest.raises(TypeError): + httpx.Request(method, url, content={"a": "b"}) # type: ignore + @pytest.mark.asyncio async def test_multipart_multiple_files_single_input_content():