resolved RESOLVED LOOP PASS · 19 tool calls · 85 s · psf/requests
Add hasattr checks for remaining protocol isinstance checks This PR is a successor to #7502. The proxied-read case appears in the standard library which points to it being a pretty well established pattern. A quick search shows there are two other places this was missed in. Rather than put `or hasattr(data, "read")` everywhere we use want to use `SupportsRead`, this combines it into a single check that scopes typing correctly for proxy objects. That should hopefully avoid missing it for future checks as well.
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"SupportsRead\" src/requests | head -20"
} | {"status": "ok", "stdout": "src/requests/models.py:39:from ._types import SupportsRead as _SupportsRead\nsrc/requests/models.py:144: data: _t.SupportsRead[str |full{"status": "ok", "stdout": "src/requests/models.py:39:from ._types import SupportsRead as _SupportsRead\nsrc/requests/models.py:144: data: _t.SupportsRead[str | bytes],\nsrc/requests/models.py:145: ) -> _t.SupportsRead[str | bytes]: ...\nsrc/requests/models.py:154: ) -> str | bytes | _t.SupportsRead[str | bytes]:\nsrc/requests/models.py:164: elif isinstance(data, _SupportsRead):\nsrc/requests/models.py:241: elif isinstance(fp, _SupportsRead) or hasattr(fp, \"read\"):\nsrc/requests/models.py:644: if isinstance(data, basestring) or isinstance(data, _SupportsRead):\nsrc/requests/_types.py:28:class SupportsRead(Protocol[_T_co]):\nsrc/requests/_types.py:95: StreamDataType: TypeAlias = SupportsRead[str | bytes]\nsrc/requests/_types.py:104: | SupportsRead[str | bytes]\nsrc/requests/_types.py:109: bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None\nsrc/requests/_types.py:118: _FileContent: TypeAlias = SupportsRead[str | bytes] | str | bytes\n", "stderr": "", "exit_code": 0} |
| 2-4 ×3 | read_file | {
"filepath": "src/requests/_types.py",
"start_line": "20",
"end_line": "40"
} | {"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}full{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"} |
| 5 | read_file | {
"filepath": "src/requests/_types.py"
} | {"status": "ok", "filepath": "src/requests/_types.py", "content": "\"\"\"\nrequests._types\n~~~~~~~~~~~~~~~\n\nThis module contains type aliases used internallyfull{"status": "ok", "filepath": "src/requests/_types.py", "content": "\"\"\"\nrequests._types\n~~~~~~~~~~~~~~~\n\nThis module contains type aliases used internally by the Requests library.\nThese types are not part of the public API and must not be relied upon\nby external code.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence\nfrom typing import (\n TYPE_CHECKING,\n Any,\n Protocol,\n TypeAlias,\n TypeVar,\n runtime_checkable,\n)\n\n_T_co = TypeVar(\"_T_co\", covariant=True)\n_KT_co = TypeVar(\"_KT_co\", covariant=True)\n_VT_co = TypeVar(\"_VT_co\", covariant=True)\n\n\n@runtime_checkable\nclass SupportsRead(Protocol[_T_co]):\n def read(self, length: int = ..., /) -> _T_co: ...\n\n\n@runtime_checkable\nclass SupportsItems(Protocol[_KT_co, _VT_co]):\n def items(self) -> Iterable[tuple[_KT_co, _VT_co]]: ...\n\n\n# These are needed at runtime for default_hooks() return type\nHookType: TypeAlias = Callable[[\"Response\"], Any]\nHooksInputType: TypeAlias = Mapping[str, Iterable[HookType] | HookType]\n\n\ndef is_prepared(request: PreparedRequest) -> TypeIs[_ValidatedRequest]:\n \"\"\"Verify a PreparedRequest has been fully prepared.\"\"\"\n if TYPE_CHECKING:\n return request.url is not None and request.method is not None\n # noop at runtime to avoid AssertionError\n return True\n\n\nif TYPE_CHECKING:\n from http.cookiejar import CookieJar\n from typing import TypeAlias, TypedDict\n\n from typing_extensions import (\n Buffer, # TODO: move to collections.abc when Python >= 3.12\n TypeIs, # TODO: move to typing when Python >= 3.13\n )\n\n from .auth import AuthBase\n from .cookies import RequestsCookieJar\n from .models import PreparedRequest, Response\n from .structures import CaseInsensitiveDict\n\n class _ValidatedRequest(PreparedRequest):\n \"\"\"Subtype asserting a PreparedRequest has been fully prep |
| 6 | read_file | {
"filepath": "src/requests/models.py"
} | {"status": "ok", "filepath": "src/requests/models.py", "content": "\"\"\"\nrequests.models\n~~~~~~~~~~~~~~~\n\nThis module contains the primary objects that powfull{"status": "ok", "filepath": "src/requests/models.py", "content": "\"\"\"\nrequests.models\n~~~~~~~~~~~~~~~\n\nThis module contains the primary objects that power Requests.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport datetime\n\n# Import encoding now, to avoid implicit import later.\n# Implicit import within threads may cause LookupError when standard library is in a ZIP,\n# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.\nimport encodings.idna # noqa: F401\nfrom collections.abc import Callable, Generator, Iterable, Iterator, Mapping\nfrom io import UnsupportedOperation\nfrom typing import (\n TYPE_CHECKING,\n Any,\n Final,\n Literal,\n cast,\n overload,\n)\n\nfrom urllib3.exceptions import (\n DecodeError,\n LocationParseError,\n ProtocolError,\n ReadTimeoutError,\n SSLError,\n)\nfrom urllib3.fields import RequestField\nfrom urllib3.filepost import encode_multipart_formdata\nfrom urllib3.util import parse_url\n\nfrom ._internal_utils import to_native_string, unicode_is_ascii\nfrom ._types import SupportsRead as _SupportsRead\nfrom .auth import HTTPBasicAuth\nfrom .compat import (\n JSONDecodeError,\n basestring,\n builtin_str,\n chardet,\n cookielib,\n urlencode,\n urlsplit,\n urlunparse,\n)\nfrom .compat import json as complexjson\nfrom .cookies import (\n _copy_cookie_jar,\n cookiejar_from_dict,\n get_cookie_header,\n)\nfrom .exceptions import (\n ChunkedEncodingError,\n ConnectionError,\n ContentDecodingError,\n HTTPError,\n InvalidJSONError,\n InvalidURL,\n MissingSchema,\n StreamConsumedError,\n)\nfrom .exceptions import JSONDecodeError as RequestsJSONDecodeError\nfrom .exceptions import SSLError as RequestsSSLError\nfrom .hooks import default_hooks\nfrom .status_codes import codes\nfrom .structures import CaseInsensitiveDict\nfrom .utils import (\n check_header_validity,\n get_auth_from_url,\n guess_filename,\n guess_ |
| 7-9 ×3 | read_file | {
"filepath": "src/requests/models.py",
"start_line": "151",
"end_line": "250"
} | {"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}full{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"} |
| 10 | read_file | {
"filepath": "src/requests/models.py"
} | {"status": "ok", "filepath": "src/requests/models.py", "content": "\"\"\"\nrequests.models\n~~~~~~~~~~~~~~~\n\nThis module contains the primary objects that powfull{"status": "ok", "filepath": "src/requests/models.py", "content": "\"\"\"\nrequests.models\n~~~~~~~~~~~~~~~\n\nThis module contains the primary objects that power Requests.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport datetime\n\n# Import encoding now, to avoid implicit import later.\n# Implicit import within threads may cause LookupError when standard library is in a ZIP,\n# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.\nimport encodings.idna # noqa: F401\nfrom collections.abc import Callable, Generator, Iterable, Iterator, Mapping\nfrom io import UnsupportedOperation\nfrom typing import (\n TYPE_CHECKING,\n Any,\n Final,\n Literal,\n cast,\n overload,\n)\n\nfrom urllib3.exceptions import (\n DecodeError,\n LocationParseError,\n ProtocolError,\n ReadTimeoutError,\n SSLError,\n)\nfrom urllib3.fields import RequestField\nfrom urllib3.filepost import encode_multipart_formdata\nfrom urllib3.util import parse_url\n\nfrom ._internal_utils import to_native_string, unicode_is_ascii\nfrom ._types import SupportsRead as _SupportsRead\nfrom .auth import HTTPBasicAuth\nfrom .compat import (\n JSONDecodeError,\n basestring,\n builtin_str,\n chardet,\n cookielib,\n urlencode,\n urlsplit,\n urlunparse,\n)\nfrom .compat import json as complexjson\nfrom .cookies import (\n _copy_cookie_jar,\n cookiejar_from_dict,\n get_cookie_header,\n)\nfrom .exceptions import (\n ChunkedEncodingError,\n ConnectionError,\n ContentDecodingError,\n HTTPError,\n InvalidJSONError,\n InvalidURL,\n MissingSchema,\n StreamConsumedError,\n)\nfrom .exceptions import JSONDecodeError as RequestsJSONDecodeError\nfrom .exceptions import SSLError as RequestsSSLError\nfrom .hooks import default_hooks\nfrom .status_codes import codes\nfrom .structures import CaseInsensitiveDict\nfrom .utils import (\n check_header_validity,\n get_auth_from_url,\n guess_filename,\n guess_ |
| 11 | run_command | {
"command": "grep -n \"isinstance(data, _SupportsRead)\" src/requests/models.py"
} | {"status": "ok", "stdout": "164: elif isinstance(data, _SupportsRead):\n644: if isinstance(data, basestring) or isinstance(data, _SupportsRead):\n", "stderr": "full{"status": "ok", "stdout": "164: elif isinstance(data, _SupportsRead):\n644: if isinstance(data, basestring) or isinstance(data, _SupportsRead):\n", "stderr": "", "exit_code": 0} |
| 12 | run_command | {
"command": "grep -n \"isinstance(fp, _SupportsRead)\" src/requests/models.py"
} | {"status": "ok", "stdout": "241: elif isinstance(fp, _SupportsRead) or hasattr(fp, \"read\"):\n", "stderr": "", "exit_code": 0}full{"status": "ok", "stdout": "241: elif isinstance(fp, _SupportsRead) or hasattr(fp, \"read\"):\n", "stderr": "", "exit_code": 0} |
| 13-15 ×3 | read_file | {
"end_line": "170",
"filepath": "src/requests/models.py",
"start_line": "160"
} | {"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}full{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"} |
| 16 | run_command | {
"command": "sed -n '160,170p' src/requests/models.py"
} | {"status": "ok", "stdout": " \"\"\"\n\n if isinstance(data, (str, bytes)):\n return data\n elif isinstance(data, _SupportsRead):\n return data\n elif hasattr(dafull{"status": "ok", "stdout": " \"\"\"\n\n if isinstance(data, (str, bytes)):\n return data\n elif isinstance(data, _SupportsRead):\n return data\n elif hasattr(data, \"__iter__\"):\n result: list[tuple[bytes, bytes]] = []\n for k, vs in to_key_val_list(data):\n if isinstance(vs, basestring) or not hasattr(vs, \"__iter__\"):\n vs = [vs]\n", "stderr": "", "exit_code": 0} |
| 17 | run_command | {
"command": "sed -n '640,650p' src/requests/models.py"
} | {"status": "ok", "stdout": " (body, content_type) = self._encode_files(files, raw_data)\n else:\n if raw_data:\n body = self._encode_params(raw_data)\n if isinsfull{"status": "ok", "stdout": " (body, content_type) = self._encode_files(files, raw_data)\n else:\n if raw_data:\n body = self._encode_params(raw_data)\n if isinstance(data, basestring) or isinstance(data, _SupportsRead):\n content_type = None\n else:\n content_type = \"application/x-www-form-urlencoded\"\n\n self.prepare_content_length(body)\n\n", "stderr": "", "exit_code": 0} |
| 18 | edit_file | {
"filepath": "src/requests/models.py",
"new_string": " elif isinstance(data, _SupportsRead) or hasattr(data, \"read\"):",
"old_string": " elif isinstance(data, _SupportsRead):"
} | {"status": "ok", "filepath": "src/requests/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/models.py\n+++ b/src/requests/models.pfull{"status": "ok", "filepath": "src/requests/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/models.py\n+++ b/src/requests/models.py\n@@ -161,7 +161,7 @@\n \n if isinstance(data, (str, bytes)):\n return data\n- elif isinstance(data, _SupportsRead):\n+ elif isinstance(data, _SupportsRead) or hasattr(data, \"read\"):\n return data\n elif hasattr(data, \"__iter__\"):\n result: list[tuple[bytes, bytes]] = []\n", "is_truncated": false} |
| 19 | edit_file | {
"filepath": "src/requests/models.py",
"new_string": " if isinstance(data, basestring) or isinstance(data, _SupportsRead) or hasattr(data, \"read\"):",
"old_string": " if isinstance(data, basestring) or isinstance(data, _SupportsRead):"
} | {"status": "ok", "filepath": "src/requests/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/models.py\n+++ b/src/requests/models.pfull{"status": "ok", "filepath": "src/requests/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/models.py\n+++ b/src/requests/models.py\n@@ -641,7 +641,7 @@\n else:\n if raw_data:\n body = self._encode_params(raw_data)\n- if isinstance(data, basestring) or isinstance(data, _SupportsRead):\n+ if isinstance(data, basestring) or isinstance(data, _SupportsRead) or hasattr(data, \"read\"):\n content_type = None\n else:\n content_type = \"application/x-www-form-urlencoded\"\n", "is_truncated": false} |
| 20 | submit_patch | {} | {"status": "ok", "patch_size": 1049, "files_changed": 1}full{"status": "ok", "patch_size": 1049, "files_changed": 1} |
diff --git a/src/requests/models.py b/src/requests/models.py
index ebeba1e3..e798d91d 100644
--- a/src/requests/models.py
+++ b/src/requests/models.py
@@ -161,7 +161,7 @@ class RequestEncodingMixin:
if isinstance(data, (str, bytes)):
return data
- elif isinstance(data, _SupportsRead):
+ elif isinstance(data, _SupportsRead) or hasattr(data, "read"):
return data
elif hasattr(data, "__iter__"):
result: list[tuple[bytes, bytes]] = []
@@ -641,7 +641,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
else:
if raw_data:
body = self._encode_params(raw_data)
- if isinstance(data, basestring) or isinstance(data, _SupportsRead):
+ if isinstance(data, basestring) or isinstance(data, _SupportsRead) or hasattr(data, "read"):
content_type = None
else:
content_type = "application/x-www-form-urlencoded"
........................................................................ [ 21%]
.................................................spytest-httpbin server hit an exception serving request: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:1032)
attempting to ignore so the rest of the tests can run
...................... [ 42%]
........................................................................ [ 63%]
..........................x............................................. [ 84%]
..................................................... [100%]
=============================== warnings summary ===============================
../../../../../../kaggle/tmp/envs/requests/lib/python3.13/site-packages/_pytest/config/__init__.py:1464
/kaggle/tmp/envs/requests/lib/python3.13/site-packages/_pytest/config/__init__.py:1464: PytestConfigWarning: Unknown config option: timeout
self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
tests/test_requests.py::TestRequests::test_set_basicauth[42-42]
tests/test_requests.py::TestRequests::test_set_basicauth[42-42]
/tmp/swe_work/eval6_submission/requests_7505/b/workspace/src/requests/auth.py:45: DeprecationWarning: Non-string usernames will no longer be supported in Requests 3.0.0. Please convert the object you've passed in (42) to a string or bytes object in the near future to avoid problems.
warnings.warn(
tests/test_requests.py::TestRequests::test_set_basicauth[42-42]
tests/test_requests.py::TestRequests::test_set_basicauth[42-42]
/tmp/swe_work/eval6_submission/requests_7505/b/workspace/src/requests/auth.py:55: DeprecationWarning: Non-string passwords will no longer be supported in Requests 3.0.0. Please convert the object you've passed in (<class 'int'>) to a string or bytes object in the near future to avoid problems.
warnings.warn(
tests/test_requests.py::TestRequests::test_set_basicauth[None-None]
tests/test_requests.py::TestRequests::test_set_basicauth[None-None]
/tmp/swe_work/eval6_submission/requests_7505/b/workspace/src/requests/auth.py:45: DeprecationWarning: Non-string usernames will no longer be supported in Requests 3.0.0. Please convert the object you've passed in (None) to a string or bytes object in the near future to avoid problems.
warnings.warn(
tests/test_requests.py::TestRequests::test_set_basicauth[None-None]
tests/test_requests.py::TestRequests::test_set_basicauth[None-None]
/tmp/swe_work/eval6_submission/requests_7505/b/workspace/src/requests/auth.py:55: DeprecationWarning: Non-string passwords will no longer be supported in Requests 3.0.0. Please convert the object you've passed in (<class 'NoneType'>) to a string or bytes object in the near future to avoid problems.
warnings.warn(
tests/test_requests.py::TestPreparingURLs::test_different_connection_pool_for_tls_settings_verify_True
tests/test_requests.py::TestPreparingURLs::test_different_connection_pool_for_tls_settings_verify_bundle_expired_cert
tests/test_requests.py::TestPreparingURLs::test_different_connection_pool_for_tls_settings_verify_bundle_unexpired_cert
tests/test_requests.py::TestPreparingURLs::test_different_connection_pool_for_mtls_settings
/kaggle/tmp/envs/requests/lib/python3.13/site-packages/urllib3/connectionpool.py:1129: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
339 passed, 1 skipped, 1 xfailed, 13 warnings in 39.01s
[2026-09-25 10:49:05,872] WARNING in core: flasgger is not installed; serving the static landing page at / and skipping the Swagger UI and /spec.json.
127.0.0.1 - - [25/Sep/2026 10:49:06] "GET /get HTTP/1.1" 200 313