From: Amin Alaee Date: Fri, 13 Aug 2021 10:27:42 +0000 (+0430) Subject: enforce-upload-files-binary-type (#1736) X-Git-Tag: 0.19.0~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=20e66d204864b320ba6816b501c6dca17e101836;p=thirdparty%2Fhttpx.git enforce-upload-files-binary-type (#1736) * enforce-upload-files-binary-type * Update test_multipart.py Co-authored-by: Tom Christie --- diff --git a/httpx/_multipart.py b/httpx/_multipart.py index 36bae664..683e6f13 100644 --- a/httpx/_multipart.py +++ b/httpx/_multipart.py @@ -1,4 +1,5 @@ import binascii +import io import os import typing from pathlib import Path @@ -81,6 +82,9 @@ class FileField: fileobj = value content_type = guess_content_type(filename) + if isinstance(fileobj, str) or isinstance(fileobj, io.StringIO): + raise TypeError(f"Expected bytes or bytes-like object got: {type(fileobj)}") + self.filename = filename self.file = fileobj self.content_type = content_type diff --git a/httpx/_types.py b/httpx/_types.py index 75bb9006..2381996c 100644 --- a/httpx/_types.py +++ b/httpx/_types.py @@ -79,7 +79,7 @@ ResponseContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] RequestData = dict -FileContent = Union[IO[str], IO[bytes], str, bytes] +FileContent = Union[IO[bytes], bytes] FileTypes = Union[ # file (or text) FileContent, diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 3824fb6b..cd71a246 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -139,7 +139,7 @@ def test_multipart_encode(tmp_path: typing.Any) -> None: def test_multipart_encode_unicode_file_contents() -> None: - files = {"file": ("name.txt", "<únicode string>")} + files = {"file": ("name.txt", b"")} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = os.urandom(16).hex() @@ -150,7 +150,7 @@ def test_multipart_encode_unicode_file_contents() -> None: content = ( '--{0}\r\nContent-Disposition: form-data; name="file";' ' filename="name.txt"\r\n' - "Content-Type: text/plain\r\n\r\n<únicode string>\r\n" + "Content-Type: text/plain\r\n\r\n\r\n" "--{0}--\r\n" "".format(boundary).encode("utf-8") ) @@ -212,14 +212,8 @@ def test_multipart_encode_files_guesses_correct_content_type( assert content == b"".join(stream) -@pytest.mark.parametrize( - "value, output", - ((b"", ""), ("", "")), -) -def test_multipart_encode_files_allows_bytes_or_str_content( - value: typing.Union[str, bytes], output: str -) -> None: - files = {"file": ("test.txt", value, "text/plain")} +def test_multipart_encode_files_allows_bytes_content() -> None: + files = {"file": ("test.txt", b"", "text/plain")} with mock.patch("os.urandom", return_value=os.urandom(16)): boundary = os.urandom(16).hex() @@ -229,9 +223,9 @@ def test_multipart_encode_files_allows_bytes_or_str_content( content = ( '--{0}\r\nContent-Disposition: form-data; name="file"; ' 'filename="test.txt"\r\n' - "Content-Type: text/plain\r\n\r\n{1}\r\n" + "Content-Type: text/plain\r\n\r\n\r\n" "--{0}--\r\n" - "".format(boundary, output).encode("ascii") + "".format(boundary).encode("ascii") ) assert headers == { "Content-Type": f"multipart/form-data; boundary={boundary}", @@ -240,6 +234,22 @@ def test_multipart_encode_files_allows_bytes_or_str_content( assert content == b"".join(stream) +def test_multipart_encode_files_raises_exception_with_str_content() -> None: + files = {"file": ("test.txt", "", "text/plain")} + with mock.patch("os.urandom", return_value=os.urandom(16)): + + with pytest.raises(TypeError): + encode_request(data={}, files=files) # type: ignore + + +def test_multipart_encode_files_raises_exception_with_StringIO_content() -> None: + files = {"file": ("test.txt", io.StringIO("content"), "text/plain")} + with mock.patch("os.urandom", return_value=os.urandom(16)): + + with pytest.raises(TypeError): + encode_request(data={}, files=files) # type: ignore + + def test_multipart_encode_non_seekable_filelike() -> None: """ Test that special readable but non-seekable filelike objects are supported,