mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-10 17:45:35 +00:00
Serialize dict of lists, use dicts internally
There is a weird quirk in HTTP. You can send the same header twice. For this reason, headers are represented by a dict, with lists as the values. However, it appears that HTTPlib is completely incapable of sending the same header twice. This puts me in a weird position: I want to be able to accurately represent HTTP headers in cassettes, but I don't want the extra step of always having to do [0] in the general case, i.e. request.headers['key'][0] In addition, some servers sometimes send the same header more than once, and httplib *can* deal with this situation. Futhermore, I wanted to keep the request and response cassette format as similar as possible. For this reason, in cassettes I keep a dict with lists as keys, but once deserialized into VCR, I keep them as plain, naked dicts.
This commit is contained in:
@@ -6,11 +6,11 @@ def test_remove_headers():
|
|||||||
headers = {'hello': ['goodbye'], 'secret': ['header']}
|
headers = {'hello': ['goodbye'], 'secret': ['header']}
|
||||||
request = Request('GET', 'http://google.com', '', headers)
|
request = Request('GET', 'http://google.com', '', headers)
|
||||||
_remove_headers(request, ['secret'])
|
_remove_headers(request, ['secret'])
|
||||||
assert request.headers == {'hello': ['goodbye']}
|
assert request.headers == {'hello': 'goodbye'}
|
||||||
|
|
||||||
|
|
||||||
def test_remove_headers_empty():
|
def test_remove_headers_empty():
|
||||||
headers = {'hello': ['goodbye'], 'secret': ['header']}
|
headers = {'hello': 'goodbye', 'secret': 'header'}
|
||||||
request = Request('GET', 'http://google.com', '', headers)
|
request = Request('GET', 'http://google.com', '', headers)
|
||||||
_remove_headers(request, [])
|
_remove_headers(request, [])
|
||||||
assert request.headers == headers
|
assert request.headers == headers
|
||||||
|
|||||||
@@ -11,16 +11,10 @@ def test_str():
|
|||||||
def test_headers():
|
def test_headers():
|
||||||
headers = {'X-Header1': ['h1'], 'X-Header2': 'h2'}
|
headers = {'X-Header1': ['h1'], 'X-Header2': 'h2'}
|
||||||
req = Request('GET', 'http://go.com/', '', headers)
|
req = Request('GET', 'http://go.com/', '', headers)
|
||||||
assert req.headers == {'X-Header1': ['h1'], 'X-Header2': ['h2']}
|
assert req.headers == {'X-Header1': 'h1', 'X-Header2': 'h2'}
|
||||||
|
|
||||||
req.add_header('X-Header1', 'h11')
|
req.add_header('X-Header1', 'h11')
|
||||||
assert req.headers == {'X-Header1': ['h1', 'h11'], 'X-Header2': ['h2']}
|
assert req.headers == {'X-Header1': 'h11', 'X-Header2': 'h2'}
|
||||||
|
|
||||||
|
|
||||||
def test_flat_headers_dict():
|
|
||||||
headers = {'X-Header1': ['h1', 'h11'], 'X-Header2': ['h2']}
|
|
||||||
req = Request('GET', 'http://go.com/', '', headers)
|
|
||||||
assert req.flat_headers_dict() == {'X-Header1': 'h1', 'X-Header2': 'h2'}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("uri, expected_port", [
|
@pytest.mark.parametrize("uri, expected_port", [
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ from six.moves.urllib.parse import urlparse, parse_qsl
|
|||||||
|
|
||||||
|
|
||||||
class Request(object):
|
class Request(object):
|
||||||
|
"""
|
||||||
|
VCR's representation of a request.
|
||||||
|
|
||||||
|
There is a weird quirk in HTTP. You can send the same header twice. For
|
||||||
|
this reason, headers are represented by a dict, with lists as the values.
|
||||||
|
However, it appears that HTTPlib is completely incapable of sending the
|
||||||
|
same header twice. This puts me in a weird position: I want to be able to
|
||||||
|
accurately represent HTTP headers in cassettes, but I don't want the extra
|
||||||
|
step of always having to do [0] in the general case, i.e.
|
||||||
|
request.headers['key'][0]
|
||||||
|
|
||||||
|
In addition, some servers sometimes send the same header more than once,
|
||||||
|
and httplib *can* deal with this situation.
|
||||||
|
|
||||||
|
Futhermore, I wanted to keep the request and response cassette format as
|
||||||
|
similar as possible.
|
||||||
|
|
||||||
|
For this reason, in cassettes I keep a dict with lists as keys, but once
|
||||||
|
deserialized into VCR, I keep them as plain, naked dicts.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, method, uri, body, headers):
|
def __init__(self, method, uri, body, headers):
|
||||||
self.method = method
|
self.method = method
|
||||||
@@ -12,11 +32,11 @@ class Request(object):
|
|||||||
self.add_header(key, headers[key])
|
self.add_header(key, headers[key])
|
||||||
|
|
||||||
def add_header(self, key, value):
|
def add_header(self, key, value):
|
||||||
value = list(value) if isinstance(value, (tuple, list)) else [value]
|
# see class docstring for an explanation
|
||||||
self.headers.setdefault(key, []).extend(value)
|
if isinstance(value, (tuple, list)):
|
||||||
|
self.headers[key] = value[0]
|
||||||
def flat_headers_dict(self):
|
else:
|
||||||
return dict((key, self.headers[key][0]) for key in self.headers)
|
self.headers[key] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scheme(self):
|
def scheme(self):
|
||||||
@@ -64,7 +84,7 @@ class Request(object):
|
|||||||
'method': self.method,
|
'method': self.method,
|
||||||
'uri': self.uri,
|
'uri': self.uri,
|
||||||
'body': self.body,
|
'body': self.body,
|
||||||
'headers': self.headers,
|
'headers': dict(((k, [v]) for k, v in self.headers.items())),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ class VCRConnection:
|
|||||||
method=self._vcr_request.method,
|
method=self._vcr_request.method,
|
||||||
url=self._url(self._vcr_request.uri),
|
url=self._url(self._vcr_request.uri),
|
||||||
body=self._vcr_request.body,
|
body=self._vcr_request.body,
|
||||||
headers=self._vcr_request.flat_headers_dict(),
|
headers=self._vcr_request.headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
# get the response
|
# get the response
|
||||||
|
|||||||
Reference in New Issue
Block a user