← eval8_submission_v1n

requests_7505

resolved RESOLVED PASS · 19 tool calls · 97 s · psf/requests

Task input

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 calls (20)

#ToolArgumentsResult
1run_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}
2run_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(da
full
{"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}
3run_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 isins
full
{"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}
4run_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}
5run_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}
6run_command
{
 "command": "sed -n '25,35p' src/requests/_types.py"
}
{"status": "ok", "stdout": "\n\n@runtime_checkable\nclass SupportsRead(Protocol[_T_co]):\n def read(self, length: int = ..., /) -> _T_co: ...\n\n\n@runtime_chec
full
{"status": "ok", "stdout": "\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", "stderr": "", "exit_code": 0}
7run_command
{
 "command": "grep -rn \"isinstance\" src/requests | grep \"Supports\""
}
{"status": "ok", "stdout": "src/requests/models.py:164: elif isinstance(data, _SupportsRead):\nsrc/requests/models.py:241: elif isinstance(fp, _SupportsRead) or
full
{"status": "ok", "stdout": "src/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/utils.py:154:    if isinstance(d, _SupportsItems):\nsrc/requests/utils.py:401:    if isinstance(value, _SupportsItems):\n", "stderr": "", "exit_code": 0}
8run_command
{
 "command": "grep -n \"SupportsItems\" src/requests/_types.py"
}
{"status": "ok", "stdout": "33:class SupportsItems(Protocol[_KT_co, _VT_co]):\n84: SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType]\n92: KVDataType
full
{"status": "ok", "stdout": "33:class SupportsItems(Protocol[_KT_co, _VT_co]):\n84:        SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType]\n92:    KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | SupportsItems[Any, Any]\n", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "sed -n '30,40p' src/requests/_types.py"
}
{"status": "ok", "stdout": "\n\n@runtime_checkable\nclass SupportsItems(Protocol[_KT_co, _VT_co]):\n def items(self) -> Iterable[tuple[_KT_co, _VT_co]]: ...\n\n
full
{"status": "ok", "stdout": "\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", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "sed -n '150,170p' src/requests/models.py"
}
{"status": "ok", "stdout": "\n @staticmethod\n def _encode_params(\n data: _t.EncodableDataType,\n ) -> str | bytes | _t.SupportsRead[str | bytes]:\n \"\"\"Enco
full
{"status": "ok", "stdout": "\n    @staticmethod\n    def _encode_params(\n        data: _t.EncodableDataType,\n    ) -> str | bytes | _t.SupportsRead[str | bytes]:\n        \"\"\"Encode parameters in a piece of data.\n\n        Will successfully encode parameters when passed as a dict or a list of\n        2-tuples. Order is retained if data is a list of 2-tuples but arbitrary\n        if parameters are supplied as a dict.\n        \"\"\"\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}
11run_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 isins
full
{"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}
12run_command
{
 "command": "grep -n \"isinstance(d, _SupportsItems)\" src/requests/utils.py"
}
{"status": "ok", "stdout": "154: if isinstance(d, _SupportsItems):\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "154:    if isinstance(d, _SupportsItems):\n", "stderr": "", "exit_code": 0}
13run_command
{
 "command": "grep -n \"isinstance(value, _SupportsItems)\" src/requests/utils.py"
}
{"status": "ok", "stdout": "401: if isinstance(value, _SupportsItems):\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "401:    if isinstance(value, _SupportsItems):\n", "stderr": "", "exit_code": 0}
14run_command
{
 "command": "sed -n '150,160p' src/requests/utils.py"
}
{"status": "ok", "stdout": " d: _t.SupportsItems[Any, Any] | Iterable[tuple[Any, Any]],\n) -> Iterable[tuple[Any, Any]]:\n \"\"\"Returns an internal sequence di
full
{"status": "ok", "stdout": "    d: _t.SupportsItems[Any, Any] | Iterable[tuple[Any, Any]],\n) -> Iterable[tuple[Any, Any]]:\n    \"\"\"Returns an internal sequence dictionary update.\"\"\"\n\n    if isinstance(d, _SupportsItems):\n        return d.items()\n\n    return d\n\n\ndef super_len(o: Any) -> int:\n", "stderr": "", "exit_code": 0}
15run_command
{
 "command": "sed -n '395,410p' src/requests/utils.py"
}
{"status": "ok", "stdout": " if value is None:\n return None\n\n if isinstance(value, (str, bytes, bool, int)):\n raise ValueError(\"cannot encode objects that
full
{"status": "ok", "stdout": "    if value is None:\n        return None\n\n    if isinstance(value, (str, bytes, bool, int)):\n        raise ValueError(\"cannot encode objects that are not 2-tuples\")\n\n    if isinstance(value, _SupportsItems):\n        return list(value.items())\n\n    return list(value)\n\n\n# From mitsuhiko/werkzeug (used with permission).\ndef parse_list_header(value: str) -> list[str]:\n    \"\"\"Parse lists as described by RFC 2068 Section 2.\n\n", "stderr": "", "exit_code": 0}
16edit_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.p
full
{"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}
17edit_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.p
full
{"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}
18edit_file
{
 "filepath": "src/requests/utils.py",
 "new_string": "    if isinstance(d, _SupportsItems) or hasattr(d, \"items\"):",
 "old_string": "    if isinstance(d, _SupportsItems):"
}
{"status": "ok", "filepath": "src/requests/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/utils.py\n+++ b/src/requests/utils.py\n
full
{"status": "ok", "filepath": "src/requests/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/utils.py\n+++ b/src/requests/utils.py\n@@ -151,7 +151,7 @@\n ) -> Iterable[tuple[Any, Any]]:\n     \"\"\"Returns an internal sequence dictionary update.\"\"\"\n \n-    if isinstance(d, _SupportsItems):\n+    if isinstance(d, _SupportsItems) or hasattr(d, \"items\"):\n         return d.items()\n \n     return d\n", "is_truncated": false}
19edit_file
{
 "filepath": "src/requests/utils.py",
 "new_string": "    if isinstance(value, _SupportsItems) or hasattr(value, \"items\"):",
 "old_string": "    if isinstance(value, _SupportsItems):"
}
{"status": "ok", "filepath": "src/requests/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/utils.py\n+++ b/src/requests/utils.py\n
full
{"status": "ok", "filepath": "src/requests/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/src/requests/utils.py\n+++ b/src/requests/utils.py\n@@ -398,7 +398,7 @@\n     if isinstance(value, (str, bytes, bool, int)):\n         raise ValueError(\"cannot encode objects that are not 2-tuples\")\n \n-    if isinstance(value, _SupportsItems):\n+    if isinstance(value, _SupportsItems) or hasattr(value, \"items\"):\n         return list(value.items())\n \n     return list(value)\n", "is_truncated": false}
20submit_patch
{}
{"status": "ok", "patch_size": 1818, "files_changed": 2}
full
{"status": "ok", "patch_size": 1818, "files_changed": 2}

Patch

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"
diff --git a/src/requests/utils.py b/src/requests/utils.py
index fff6edf4..883240f4 100644
--- a/src/requests/utils.py
+++ b/src/requests/utils.py
@@ -151,7 +151,7 @@ def dict_to_sequence(
 ) -> Iterable[tuple[Any, Any]]:
     """Returns an internal sequence dictionary update."""
 
-    if isinstance(d, _SupportsItems):
+    if isinstance(d, _SupportsItems) or hasattr(d, "items"):
         return d.items()
 
     return d
@@ -398,7 +398,7 @@ def to_key_val_list(
     if isinstance(value, (str, bytes, bool, int)):
         raise ValueError("cannot encode objects that are not 2-tuples")
 
-    if isinstance(value, _SupportsItems):
+    if isinstance(value, _SupportsItems) or hasattr(value, "items"):
         return list(value.items())
 
     return list(value)

Test output

show
........................................................................ [ 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/eval8_submission_v1n/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/eval8_submission_v1n/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/eval8_submission_v1n/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/eval8_submission_v1n/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 64.57s (0:01:04)
[2026-09-26 01:35:35,766] 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 - - [26/Sep/2026 01:35:36] "GET /basic-auth/user/pass HTTP/1.1" 401 0
127.0.0.1 - - [26/Sep/2026 01:35:36] "GET /cookies/set?cookie=value HTTP/1.1" 302 203