Skip to content

Commit c25a1c8

Browse files
Fix issue with custom adapters (#643)
Fix issue with custom adapters when positional arguments are sent instead of kwargs. See #642
1 parent c5d193f commit c25a1c8

File tree

3 files changed

+78
-23
lines changed

3 files changed

+78
-23
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
------
33

44
* Updated dependency to urllib3>=2 and requests>=2.30.0. See #635
5+
* Fixed issue when custom adapters were sending only positional args. See #642
56

67

78
0.23.1

responses/__init__.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,9 +1099,27 @@ def start(self) -> None:
10991099
return
11001100

11011101
def unbound_on_send(
1102-
adapter: "HTTPAdapter", request: "PreparedRequest", *a: Any, **kwargs: Any
1102+
adapter: "HTTPAdapter",
1103+
request: "PreparedRequest",
1104+
*args: Any,
1105+
**kwargs: Any,
11031106
) -> "models.Response":
1104-
return self._on_request(adapter, request, *a, **kwargs)
1107+
if args:
1108+
# that probably means that the request was sent from the custom adapter
1109+
# It is fully legit to send positional args from adapter, although,
1110+
# `requests` implementation does it always with kwargs
1111+
# See for more info: https://github.com/getsentry/responses/issues/642
1112+
try:
1113+
kwargs["stream"] = args[0]
1114+
kwargs["timeout"] = args[1]
1115+
kwargs["verify"] = args[2]
1116+
kwargs["cert"] = args[3]
1117+
kwargs["proxies"] = args[4]
1118+
except IndexError:
1119+
# not all kwargs are required
1120+
pass
1121+
1122+
return self._on_request(adapter, request, **kwargs)
11051123

11061124
self._patcher = std_mock.patch(target=self.target, new=unbound_on_send)
11071125
self._patcher.start()

responses/tests/test_responses.py

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -801,34 +801,70 @@ def test_base_response_get_response():
801801
resp.get_response(requests.PreparedRequest())
802802

803803

804-
def test_custom_adapter():
805-
@responses.activate
806-
def run():
807-
url = "http://example.com"
808-
responses.add(responses.GET, url, body=b"test")
804+
class TestAdapters:
805+
class CustomAdapter(requests.adapters.HTTPAdapter):
806+
"""Classic custom adapter."""
809807

810-
calls = [0]
808+
def send(self, *a, **k):
809+
return super().send(*a, **k)
811810

812-
class DummyAdapter(requests.adapters.HTTPAdapter):
813-
def send(self, *a, **k):
814-
calls[0] += 1
815-
return super().send(*a, **k)
811+
class PositionalArgsAdapter(requests.adapters.HTTPAdapter):
812+
"""Custom adapter that sends only positional args.
813+
See https://github.com/getsentry/responses/issues/642 for more into.
814+
"""
816815

817-
# Test that the adapter is actually used
818-
session = requests.Session()
819-
session.mount("http://", DummyAdapter())
816+
def send(
817+
self,
818+
request,
819+
stream=False,
820+
timeout=None,
821+
verify=True,
822+
cert=None,
823+
proxies=None,
824+
):
825+
return super().send(request, stream, timeout, verify, cert, proxies)
826+
827+
class PositionalArgsIncompleteAdapter(requests.adapters.HTTPAdapter):
828+
"""Custom adapter that sends only positional args.
829+
Not all arguments are forwarded to the send method.
830+
See https://github.com/getsentry/responses/issues/642 for more into.
831+
"""
820832

821-
session.get(url, allow_redirects=False)
822-
assert calls[0] == 1
833+
def send(
834+
self,
835+
request,
836+
stream=False,
837+
timeout=None,
838+
verify=True,
839+
# following args are intentionally not forwarded
840+
cert=None,
841+
proxies=None,
842+
):
843+
return super().send(request, stream, timeout, verify)
844+
845+
@pytest.mark.parametrize(
846+
"adapter_class",
847+
(CustomAdapter, PositionalArgsAdapter, PositionalArgsIncompleteAdapter),
848+
)
849+
def test_custom_adapter(self, adapter_class):
850+
"""Test basic adapter implementation and that responses can patch them properly."""
823851

824-
# Test that the response is still correctly emulated
825-
session = requests.Session()
826-
session.mount("http://", DummyAdapter())
852+
@responses.activate
853+
def run():
854+
url = "http://example.com"
855+
responses.add(responses.GET, url, body=b"test adapter")
827856

828-
resp = session.get(url)
829-
assert_response(resp, "test")
857+
# Test that the adapter is actually used
858+
session = requests.Session()
859+
adapter = adapter_class()
860+
session.mount("http://", adapter)
861+
with patch.object(adapter, "send", side_effect=adapter.send) as mock_send:
862+
resp = session.get(url, allow_redirects=False)
830863

831-
run()
864+
assert mock_send.call_count == 1
865+
assert_response(resp, "test adapter")
866+
867+
run()
832868

833869

834870
def test_responses_as_context_manager():

0 commit comments

Comments
 (0)