Requests 2.34.0 changed the stream detection in PreparedRequest.prepare_body() from hasattr(data, "__iter__") to isinstance(data, Iterable). This breaks file-like wrappers that expose __iter__/read/tell/seek via __getattr__ delegation (e.g. tqdm.wrapattr), because isinstance(..., Iterable) only checks the class MRO, not dynamic attribute delegation. As a result, _body_position is never recorded and the body cannot be rewound on 307/308 redirects.
Expected Result
Upload completes successfully through the redirect, as it did in 2.33.1.
Actual Result
The redirected request sends an empty/consumed body. The server receives no data and the request times out:
requests.exceptions.ReadTimeout: HTTPSConnectionPool(...): Read timed out. (read timeout=30)
Reproduction Steps
import os
import requests
from tqdm import tqdm
with open("testfile.txt", "wb") as f:
f.write(b"hello world " * 1000)
with open("testfile.txt", "rb") as f:
with tqdm.wrapattr(f, "read", total=os.path.getsize("testfile.txt")) as wrapped:
# Any endpoint that 307 redirects to a presigned S3 URL
requests.put("https://your-api.example.com/upload", data=wrapped, timeout=30)
System Information
$ python -m requests.help
{
"chardet": {
"version": null
},
"charset_normalizer": {
"version": "3.4.7"
},
"cryptography": {
"version": ""
},
"idna": {
"version": "3.13"
},
"implementation": {
"name": "CPython",
"version": "3.13.5"
},
"platform": {
"release": "24.6.0",
"system": "Darwin"
},
"pyOpenSSL": {
"openssl_version": "",
"version": null
},
"requests": {
"version": "2.34.0"
},
"system_ssl": {
"version": "30000120"
},
"urllib3": {
"version": "2.5.0"
},
"using_charset_normalizer": true,
"using_pyopenssl": false
}
Requests 2.34.0 changed the stream detection in PreparedRequest.prepare_body() from
hasattr(data, "__iter__")toisinstance(data, Iterable). This breaks file-like wrappers that expose__iter__/read/tell/seekvia__getattr__ delegation(e.g. tqdm.wrapattr), becauseisinstance(..., Iterable)only checks the class MRO, not dynamic attribute delegation. As a result,_body_positionis never recorded and the body cannot be rewound on 307/308 redirects.Expected Result
Upload completes successfully through the redirect, as it did in 2.33.1.
Actual Result
The redirected request sends an empty/consumed body. The server receives no data and the request times out:
Reproduction Steps
System Information
{ "chardet": { "version": null }, "charset_normalizer": { "version": "3.4.7" }, "cryptography": { "version": "" }, "idna": { "version": "3.13" }, "implementation": { "name": "CPython", "version": "3.13.5" }, "platform": { "release": "24.6.0", "system": "Darwin" }, "pyOpenSSL": { "openssl_version": "", "version": null }, "requests": { "version": "2.34.0" }, "system_ssl": { "version": "30000120" }, "urllib3": { "version": "2.5.0" }, "using_charset_normalizer": true, "using_pyopenssl": false }