vcrpy icon indicating copy to clipboard operation
vcrpy copied to clipboard

[BUG] HTTPX Binary upload data unsuported

Open 07pepa opened this issue 1 year ago • 1 comments

I am trying to migrate one library from requests to http and it seems httpx portion have some bugs

folowing casete cant be used with httpx https://github.com/billdeitrick/pypco/blob/master/tests/cassettes/TestPublicRequestFunctions.test_upload_and_use_file.yaml

because it tries to decode the binary string (that is nonsense)

Error
Traceback (most recent call last):
  File "pypco\pypco\pco.py", line 251, in request_response
    response = self._do_url_managed_request(method, url, payload, upload, **params)
  File "pypco\pypco\pco.py", line 223, in _do_url_managed_request
    return self._do_ratelimit_managed_request(method, url, payload, upload, **params)
  File "pypco\pypco\pco.py", line 184, in _do_ratelimit_managed_request
    response = self._do_timeout_managed_request(method, url, payload, upload, **params)
  File "pypco\pypco\pco.py", line 145, in _do_timeout_managed_request
    return self._do_request(method, url, payload, upload, **params)
  File "pypco\pypco\pco.py", line 110, in _do_request
    response = self.session.request(
  File ".virtualenvs\pypco-EZZlDlNc\lib\site-packages\httpx\_client.py", line 815, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
  File ".virtualenvs\pypco-EZZlDlNc\lib\site-packages\vcr\stubs\httpx_stubs.py", line 168, in _inner_send
    return _sync_vcr_send(cassette, real_send, *args, **kwargs)
  File ".virtualenvs\pypco-EZZlDlNc\lib\site-packages\vcr\stubs\httpx_stubs.py", line 155, in _sync_vcr_send
    vcr_request, response = _shared_vcr_send(cassette, real_send, *args, **kwargs)
  File ".virtualenvs\pypco-EZZlDlNc\lib\site-packages\vcr\stubs\httpx_stubs.py", line 81, in _shared_vcr_send
    vcr_request = _make_vcr_request(real_request, **kwargs)
  File ".virtualenvs\pypco-EZZlDlNc\lib\site-packages\vcr\stubs\httpx_stubs.py", line 72, in _make_vcr_request
    body = httpx_request.read().decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 137: invalid start byte

07pepa avatar Sep 23 '22 09:09 07pepa

Probably related: https://github.com/ktosiek/pytest-vcr/issues/46

llybin avatar Oct 04 '22 14:10 llybin

indeed it is

07pepa avatar Oct 11 '22 15:10 07pepa

The same thing happens when trying to record a cassette with httpx, where binary request data causes a UnicodeDecodeError. Here's a simple reproduction script that does the same thing successfully both with requests and with httpx outside of a vcr.use_cassette context.

import httpx
import requests
import vcr


requests_response = requests.post('http://example.com', data=b'\xff')
print(requests_response.status_code)

httpx_response = httpx.post('http://example.com', data=b'\xff')
print(httpx_response.status_code)

with vcr.use_cassette('requests.yaml'):
    requests_response = requests.post('http://example.com', data=b'\xff')
    print(requests_response.status_code)

with vcr.use_cassette('httpx.yaml'):
    httpx_response = httpx.post('http://example.com', data=b'\xff')
    print(httpx_response.status_code)

Expected output:

200
200
200
200

Actual output:

200
200
200
Traceback (most recent call last):
  File "/Users/rberryhill/.pyenv/versions/3.8.13/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/rberryhill/.pyenv/versions/3.8.13/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/Users/rberryhill/src/vcrbug/bug.py", line 18, in <module>
    httpx_response = httpx.post('http://example.com', data=b'\xff')
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/httpx/_api.py", line 304, in post
    return request(
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/httpx/_api.py", line 100, in request
    return client.request(
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/httpx/_client.py", line 821, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/vcr/stubs/httpx_stubs.py", line 168, in _inner_send
    return _sync_vcr_send(cassette, real_send, *args, **kwargs)
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/vcr/stubs/httpx_stubs.py", line 155, in _sync_vcr_send
    vcr_request, response = _shared_vcr_send(cassette, real_send, *args, **kwargs)
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/vcr/stubs/httpx_stubs.py", line 81, in _shared_vcr_send
    vcr_request = _make_vcr_request(real_request, **kwargs)
  File "/Users/rberryhill/src/vcrbug/venv/lib/python3.8/site-packages/vcr/stubs/httpx_stubs.py", line 72, in _make_vcr_request
    body = httpx_request.read().decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

This is with python 3.8.13, httpx==0.23.3, vcrpy==4.2.1.

ryanberryhill avatar Feb 17 '23 16:02 ryanberryhill

Can we use errors="surrogateescape" here?

gotmax23 avatar Aug 13 '23 01:08 gotmax23

I still get the same error if i upload a file via httpx in a test with vcrpy.

  • Python 3.11.8
  • vcrpy 6.0.1
  • httpx 0.27.0
/usr/local/lib/python3.11/site-packages/statsd/client/timer.py:41: in _wrapped
    return f(*args, **kwargs)
src/common/client/example/client.py:39: in analyse
    result = self._httpx_client.post(
/usr/local/lib/python3.11/site-packages/httpx/_client.py:1145: in post
    return self.request(
/usr/local/lib/python3.11/site-packages/httpx/_client.py:827: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
/usr/local/lib/python3.11/site-packages/sentry_sdk/integrations/httpx.py:84: in send
    rv = real_send(self, request, **kwargs)
/usr/local/lib/python3.11/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
/usr/local/lib/python3.11/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
/usr/local/lib/python3.11/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
/usr/local/lib/python3.11/site-packages/vcr/stubs/httpx_stubs.py:184: in _inner_send
    return _sync_vcr_send(cassette, real_send, *args, **kwargs)
/usr/local/lib/python3.11/site-packages/vcr/stubs/httpx_stubs.py:170: in _sync_vcr_send
    vcr_request, response = _shared_vcr_send(cassette, real_send, *args, **kwargs)
/usr/local/lib/python3.11/site-packages/vcr/stubs/httpx_stubs.py:117: in _shared_vcr_send
    vcr_request = _make_vcr_request(real_request, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

httpx_request = <Request('POST', 'http://example.com/upload')>, kwargs = {}

    def _make_vcr_request(httpx_request, **kwargs):
>       body = httpx_request.read().decode("utf-8")
E       UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 161: invalid start byte

76tr75b avatar Feb 22 '24 14:02 76tr75b

Ok, my issue is fixed by this PR.

76tr75b avatar Feb 26 '24 14:02 76tr75b