1
0
mirror of https://github.com/kevin1024/vcrpy.git synced 2025-12-08 16:53:23 +00:00

Compare commits

..

6 Commits

Author SHA1 Message Date
Kevin McCarthy
7d3f887ebc Version bump to 0.5.0 2013-12-01 14:49:48 -10:00
Kevin McCarthy
49929e3064 formatting fixes 2013-12-01 14:38:46 -10:00
Kevin McCarthy
188b57a2fa Fix API by adding 'responses_of' method
`responses_of` replaces `response_of`, since each request can have
several matching responses now.

This breaks backwards compatibility if you are using the
response_of method, so a version bump will be required.
2013-12-01 14:26:35 -10:00
Kevin McCarthy
b84f8e963b Fix Cryptic 'write-protected' Message
Closes #46
2013-12-01 13:46:44 -10:00
Marc Abramowitz
ea13d51677 del self.sock in VCRConnectionMixin.request
instead of in connection class constructors.

Fixes GH-48.
2013-11-26 12:12:39 -08:00
Marc Abramowitz
5ba4000f77 Add test: test_session_and_connection_close
This is a test for issue GH-48.
2013-11-26 12:07:13 -08:00
6 changed files with 61 additions and 19 deletions

View File

@@ -162,7 +162,7 @@ part of the API. The fields are as follows:
* `responses`: A list of the responses made.
* `play_count`: The number of times this cassette has had a response
played back
* `response_of(request)`: Access the response for a given request.
* `responses_of(request)`: Access the responses that match a given request
The Request object has the following properties
@@ -257,6 +257,7 @@ This library is a work in progress, so the API might change on you.
There are probably some [bugs](https://github.com/kevin1024/vcrpy/issues?labels=bug&page=1&state=open) floating around too.
##Changelog
* 0.5.0: Change the `response_of` method to `responses_of` since cassettes can now contain more than one response for a request. Since this changes the API, I'm bumping the version. Also includes 2 bugfixes: a better error message when attempting to overwrite a cassette file, and a fix for a bug with requests sessions (thanks @msabramo)
* 0.4.0: Change default request recording behavior for multiple requests. If you make the same request multiple times to the same URL, the response might be different each time (maybe the response has a timestamp in it or something), so this will make the same request multiple times and save them all. Then, when you are replaying the cassette, the responses will be played back in the same order in which they were received. If you were making multiple requests to the same URL in a cassette before version 0.4.0, you might need to regenerate your cassette files. Also, removes support for the cassette.play_count counter API, since individual requests aren't unique anymore. A cassette might contain the same request several times. Also removes secure overwrite feature since that was breaking overwriting files in Windows, and fixes a bug preventing request's automatic body decompression from working.
* 0.3.5: Fix compatibility with requests 2.x
* 0.3.4: Bugfix: close file before renaming it. This fixes an issue on Windows. Thanks @smallcode for the fix.

View File

@@ -130,3 +130,18 @@ def test_gzip(tmpdir, scheme):
with vcr.use_cassette(str(tmpdir.join('gzip.yaml'))) as cass:
assert_is_json(response.content)
def test_session_and_connection_close(tmpdir, scheme):
'''
This tests the issue in https://github.com/kevin1024/vcrpy/issues/48
If you use a requests.session and the connection is closed, then an
exception is raised in the urllib3 module vendored into requests:
`AttributeError: 'NoneType' object has no attribute 'settimeout'`
'''
with vcr.use_cassette(str(tmpdir.join('session_connection_closed.yaml'))):
session = requests.session()
resp = session.get('http://httpbin.org/get', headers={'Connection': 'close'})
resp = session.get('http://httpbin.org/get', headers={'Connection': 'close'})

View File

@@ -44,14 +44,22 @@ def test_cassette_contains():
@mock.patch('vcr.cassette.requests_match', _mock_requests_match)
def test_cassette_response_of():
def test_cassette_responses_of():
a = Cassette('test')
a.append('foo', 'bar')
assert a.response_of('foo') == 'bar'
assert a.responses_of('foo') == ['bar']
@mock.patch('vcr.cassette.requests_match', _mock_requests_match)
def test_cassette_get_missing_response():
a = Cassette('test')
with pytest.raises(KeyError):
a.response_of('foo')
a.responses_of('foo')
@mock.patch('vcr.cassette.requests_match', _mock_requests_match)
def test_cassette_cant_read_same_request_twice():
a = Cassette('test')
a.append('foo','bar')
a.play_response('foo')
with pytest.raises(KeyError):
a.play_response('foo')

View File

@@ -61,10 +61,10 @@ class Cassette(object):
self.data.append((request, response))
self.dirty = True
def response_of(self, request):
def play_response(self, request):
'''
Find the response corresponding to a request
Get the response corresponding to a request, but only if it
hasn't been played back before, and mark it as playe.d
'''
for index, (stored_request, response) in enumerate(self.data):
if requests_match(request, stored_request, self._match_on):
@@ -75,6 +75,22 @@ class Cassette(object):
# if the cassette doesn't contain the request asked for.
raise KeyError
def responses_of(self, request):
'''
Find the responses corresponding to a request.
This function isn't actually used by VCR internally, but is
provided as an external API.
'''
responses = \
[resp for req, resp in self.data if
requests_match(req, request, self._match_on)]
if responses:
return responses
# I decided that a KeyError is the best exception to raise
# if the cassette doesn't contain the request asked for.
raise KeyError
def _as_dict(self):
return {"requests": self.requests, "responses": self.responses}

View File

@@ -8,5 +8,5 @@ class FilesystemPersister(object):
dirname, filename = os.path.split(cassette_path)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
with open(cassette_path, 'w') as f:
f.write(data)
with open(cassette_path, 'w') as f:
f.write(data)

View File

@@ -56,6 +56,10 @@ class VCRConnectionMixin:
def request(self, method, url, body=None, headers=None):
'''Persist the request metadata in self._vcr_request'''
# see VCRConnectionMixin._restore_socket for the motivation here
if hasattr(self, 'sock'):
del self.sock
self._vcr_request = Request(
protocol=self._protocol,
host=self.host,
@@ -161,12 +165,17 @@ class VCRConnectionMixin:
'''Retrieve a the response'''
# Check to see if the cassette has a response for this request. If so,
# then return it
if self._vcr_request in self.cassette and self.cassette.record_mode != "all" and self.cassette.rewound:
response = self.cassette.response_of(self._vcr_request)
if self._vcr_request in self.cassette and \
self.cassette.record_mode != "all" and \
self.cassette.rewound:
response = self.cassette.play_response(self._vcr_request)
return VCRHTTPResponse(response)
else:
if self.cassette.write_protected:
raise Exception("cassette is write protected")
raise Exception(
"Can't overwrite existing cassette in \
your current record mode."
)
# Otherwise, we should send the request, then get the response
# and return it.
@@ -205,11 +214,6 @@ class VCRHTTPConnection(VCRConnectionMixin, HTTPConnection):
_baseclass = HTTPConnection
_protocol = 'http'
def __init__(self, *args, **kwargs):
HTTPConnection.__init__(self, *args, **kwargs)
# see VCRConnectionMixin._restore_socket for the motivation here
del self.sock
class VCRHTTPSConnection(VCRConnectionMixin, HTTPSConnection):
'''A Mocked class for HTTPS requests'''
@@ -223,5 +227,3 @@ class VCRHTTPSConnection(VCRConnectionMixin, HTTPSConnection):
HTTPConnection.__init__(self, *args, **kwargs)
self.key_file = kwargs.pop('key_file', None)
self.cert_file = kwargs.pop('cert_file', None)
# see VCRConnectionMixin._restore_socket for the motivation here
del self.sock