Skip to content

Commit

Permalink
Read data from file-like objects in requests (#719)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrob95 committed Aug 31, 2024
1 parent b68e513 commit 42884ca
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
16 changes: 16 additions & 0 deletions responses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,21 @@ def _parse_request_params(
params[key] = values
return params

def _read_filelike_body(
self, body: Union[str, bytes, BufferedReader, None]
) -> Union[str, bytes, None]:
# Requests/urllib support multiple types of body, including file-like objects.
# Read from the file if it's a file-like object to avoid storing a closed file
# in the call list and allow the user to compare against the data that was in the
# request.
# See GH #719
if isinstance(body, str) or isinstance(body, bytes) or body is None:
return body
# Based on https://github.com/urllib3/urllib3/blob/abbfbcb1dd274fc54b4f0a7785fd04d59b634195/src/urllib3/util/request.py#L220
if hasattr(body, "read"):
return body.read() # type: ignore[attr-defined]
return body # type: ignore[no-any-return]

def _on_request(
self,
adapter: "HTTPAdapter",
Expand All @@ -1067,6 +1082,7 @@ def _on_request(
request.params = self._parse_request_params(request.path_url) # type: ignore[attr-defined]
request.req_kwargs = kwargs # type: ignore[attr-defined]
request_url = str(request.url)
request.body = self._read_filelike_body(request.body)

match, match_failed_reasons = self._find_match(request)
resp_callback = self.response_callback
Expand Down
20 changes: 20 additions & 0 deletions responses/tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2708,3 +2708,23 @@ def run():

run()
assert_reset()


def test_file_like_body_in_request():
"""Validate that when file-like objects are used in requests the data can be accessed
in the call list. This ensures that we are not storing file handles that may be closed
by the time the user wants to assert on the data in the request. GH #719.
"""

@responses.activate
def run():
responses.add(responses.POST, "https://example.com")
with tempfile.TemporaryFile() as f:
f.write(b"test")
f.seek(0)
requests.post("https://example.com", data=f)
assert len(responses.calls) == 1
assert responses.calls[0].request.body == b"test"

run()
assert_reset()

0 comments on commit 42884ca

Please sign in to comment.