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

Compare commits

...

4 Commits

Author SHA1 Message Date
Ivan Malison
f821fed418 random whitespace 2016-01-11 15:54:13 -08:00
Ivan Malison
50246791e3 allow filtering by response 2016-01-11 15:53:04 -08:00
Ivan Malison
8a5bf23d34 better logging on cassettenotfound 2015-10-26 00:36:44 -07:00
Ivan Malison
f6b8e4f8e7 Lint cleanup 2015-10-25 20:30:33 -07:00
4 changed files with 94 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ from vcr.compat import mock, contextlib
from vcr.cassette import Cassette
from vcr.errors import UnhandledHTTPRequestError
from vcr.patch import force_reset
from vcr.matchers import path, method, query, host
from vcr.stubs import VCRHTTPSConnection
@@ -294,3 +295,18 @@ def test_use_as_decorator_on_generator():
assert httplib.HTTPConnection is not original_http_connetion
yield 2
assert list(test_function()) == [1, 2]
def test_similar_requests(tmpdir):
# WIP needs to be finished
@Cassette.use(inject=True, match_on=(path, query, host, method))
def test_function(cassette):
conn = httplib.HTTPConnection("www.python.org")
conn.request("GET", "/index.html?test=1")
conn = httplib.HTTPConnection("www.python.org")
conn.request("GET", "/index.html?test=0")
conn = httplib.HTTPConnection("www.cool.org")
conn.request("GET", "/index.html?test=0")
cassette.similar_requests()

View File

@@ -100,6 +100,21 @@ def test_vcr_before_record_response_iterable():
assert mock_filter.call_count == 1
def test_before_record_response_as_filter():
request = Request('GET', '/', '', {})
response = object() # just can't be None
# Prevent actually saving the cassette
with mock.patch('vcr.cassette.save_cassette'):
filter_all = mock.Mock(return_value=None)
vcr = VCR(before_record_response=filter_all)
with vcr.use_cassette('test') as cassette:
cassette.append(request, response)
assert cassette.data == []e
assert not cassette.dirty
def test_vcr_path_transformer():
# Regression test for #199

View File

@@ -1,6 +1,7 @@
import sys
import inspect
import logging
import operator
import sys
import wrapt
@@ -145,9 +146,33 @@ class CassetteContextDecorator(object):
return new_args_getter
class SimilarityScorer(object):
def __init__(self, matchers, request, ascending=False):
self._matchers = matchers
self._request = request
self._ascending = False
def score(self, candidate, play_count):
value = 1
total = 0
if play_count < 1:
total += value
if self._ascending:
value *= 2
for matcher in self._matchers[::-1]:
if matcher(self._request, candidate):
total += value
if self._ascending:
value *= 2
return total
class Cassette(object):
"""A container for recorded requests and responses"""
max_playcount = 1
@classmethod
def load(cls, **kwargs):
"""Instantiate and load the cassette stored at the specified path."""
@@ -165,14 +190,15 @@ class Cassette(object):
def __init__(self, path, serializer=yamlserializer, record_mode='once',
match_on=(uri, method), before_record_request=None,
before_record_response=None, custom_patches=(),
inject=False):
before_record_response=None, custom_patches=(), inject=False,
similarity_scorer_factory=None):
self._path = path
self._serializer = serializer
self._match_on = match_on
self._before_record_request = before_record_request or (lambda x: x)
self._before_record_response = before_record_response or (lambda x: x)
self._similarity_scorer_factory = similarity_scorer_factory or SimilarityScorer
self.inject = inject
self.record_mode = record_mode
self.custom_patches = custom_patches
@@ -211,6 +237,8 @@ class Cassette(object):
if not request:
return
response = self._before_record_response(response)
if response is None:
return
self.data.append((request, response))
self.dirty = True
@@ -227,6 +255,20 @@ class Cassette(object):
if requests_match(request, stored_request, self._match_on):
yield index, response
def failing_matchers(self, a, b):
return [matcher for matcher in self._match_on if not matcher(a, b)]
def similar_requests(self, request):
scorer = self._similarity_scorer_factory(self._match_on, request).score
scored_requests = [
(
stored_request,
scorer(stored_request, self.play_counts[index])
)
for index, (stored_request, response) in enumerate(self.data)
]
return sorted(scored_requests, key=operator.itemgetter(1), reverse=True)
def can_play_response_for(self, request):
request = self._before_record_request(request)
return request and request in self and \
@@ -239,7 +281,7 @@ class Cassette(object):
hasn't been played back before, and mark it as played
"""
for index, response in self._responses(request):
if self.play_counts[index] == 0:
if self.play_counts[index] < self.max_playcount:
self.play_counts[index] += 1
return response
# The cassette doesn't contain the request asked for.

View File

@@ -9,7 +9,6 @@ import six
from six.moves.http_client import (
HTTPConnection,
HTTPSConnection,
HTTPMessage,
HTTPResponse,
)
from six import BytesIO
@@ -228,12 +227,26 @@ class VCRConnection(object):
if self.cassette.write_protected and self.cassette.filter_request(
self._vcr_request
):
most_similar_request = None
failing_matchers = None
most_similar_request_info = None
try:
most_similar_request_info = self.cassette.similar_requests(self._vcr_request)
most_similar_request = most_similar_request_info[0][0]
failing_matchers = self.cassette.failing_matchers(
self._vcr_request, most_similar_request
)
except Exception as err:
print "XXXX {0}".format(err)
import ipdb; ipdb.set_trace()
raise CannotOverwriteExistingCassetteException(
"No match for the request (%r) was found. "
"Can't overwrite existing cassette (%r) in "
"your current record mode (%r)."
"your current record mode (%r). Most similar request was (%r). "
"It differed from the request according to (%r). \n\n\n(%r)"
% (self._vcr_request, self.cassette._path,
self.cassette.record_mode)
self.cassette.record_mode, most_similar_request,
failing_matchers, most_similar_request_info)
)
# Otherwise, we should send the request, then get the response
@@ -306,7 +319,7 @@ class VCRConnection(object):
def __init__(self, *args, **kwargs):
if six.PY3:
kwargs.pop('strict', None) # apparently this is gone in py3
kwargs.pop('strict', None) # apparently this is gone in py3
# need to temporarily reset here because the real connection
# inherits from the thing that we are mocking out. Take out