Skip to content

Commit f9cecf8

Browse files
committed
Add hasattr checks for remaining protocol isinstance checks
1 parent 6f205ff commit f9cecf8

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

src/requests/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def _encode_params(
161161

162162
if isinstance(data, (str, bytes)):
163163
return data
164-
elif isinstance(data, _SupportsRead):
164+
elif isinstance(data, _SupportsRead) or hasattr(data, "read"):
165165
return data
166166
elif hasattr(data, "__iter__"):
167167
result: list[tuple[bytes, bytes]] = []
@@ -641,7 +641,8 @@ def prepare_body(
641641
else:
642642
if raw_data:
643643
body = self._encode_params(raw_data)
644-
if isinstance(data, basestring) or isinstance(data, _SupportsRead):
644+
has_read = isinstance(data, _SupportsRead) or hasattr(data, "read")
645+
if isinstance(data, basestring) or has_read:
645646
content_type = None
646647
else:
647648
content_type = "application/x-www-form-urlencoded"

tests/test_requests.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,21 @@ def test_post_named_tempfile(self, httpbin):
10981098
assert r.status_code == 200
10991099
assert r.json()["files"]["file"] == "named temp file contents\n"
11001100

1101+
def test_post_getattr_proxy_read_only(self, httpbin):
1102+
1103+
class ReadProxy:
1104+
def __init__(self):
1105+
self._file = io.BytesIO(b"streamed body")
1106+
1107+
def __getattr__(self, name):
1108+
if name == "__iter__":
1109+
raise AttributeError(name)
1110+
return getattr(self._file, name)
1111+
1112+
r = requests.post(httpbin("post"), data=ReadProxy())
1113+
assert r.status_code == 200
1114+
assert r.json()["data"] == "streamed body"
1115+
11011116
@pytest.mark.parametrize(
11021117
"data",
11031118
(

0 commit comments

Comments
 (0)