mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-08 16:53:23 +00:00
Merge pull request #763 from danielnsilva/drop-unused-requests
Add an option to remove unused requests from cassette
This commit is contained in:
@@ -427,3 +427,16 @@ If you want to save the cassette only when the test succeeds, set the Cassette
|
|||||||
|
|
||||||
# Since there was an exception, the cassette file hasn't been created.
|
# Since there was an exception, the cassette file hasn't been created.
|
||||||
assert not os.path.exists('fixtures/vcr_cassettes/synopsis.yaml')
|
assert not os.path.exists('fixtures/vcr_cassettes/synopsis.yaml')
|
||||||
|
|
||||||
|
Drop unused requests
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Even if any HTTP request is changed or removed from tests, previously recorded
|
||||||
|
interactions remain in the cassette file. If set the ``drop_unused_requests``
|
||||||
|
option to ``True``, VCR will not save old HTTP interactions if they are not used.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
my_vcr = VCR(drop_unused_requests=True)
|
||||||
|
with my_vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml'):
|
||||||
|
... # your HTTP interactions here
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from urllib.request import urlopen
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import vcr
|
import vcr
|
||||||
|
from vcr.cassette import Cassette
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.online
|
@pytest.mark.online
|
||||||
@@ -85,3 +86,21 @@ def test_dont_record_on_exception(tmpdir, httpbin):
|
|||||||
assert b"Not in content" in urlopen(httpbin.url).read()
|
assert b"Not in content" in urlopen(httpbin.url).read()
|
||||||
|
|
||||||
assert not os.path.exists(str(tmpdir.join("dontsave2.yml")))
|
assert not os.path.exists(str(tmpdir.join("dontsave2.yml")))
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_drop_unused_requests(tmpdir, httpbin):
|
||||||
|
my_vcr = vcr.VCR(drop_unused_requests=True)
|
||||||
|
file = str(tmpdir.join("test.yaml"))
|
||||||
|
|
||||||
|
with my_vcr.use_cassette(file):
|
||||||
|
urlopen(httpbin.url)
|
||||||
|
urlopen(httpbin.url + "/get")
|
||||||
|
|
||||||
|
cassette = Cassette.load(path=file)
|
||||||
|
assert len(cassette) == 2
|
||||||
|
|
||||||
|
with my_vcr.use_cassette(file):
|
||||||
|
urlopen(httpbin.url)
|
||||||
|
|
||||||
|
cassette = Cassette.load(path=file)
|
||||||
|
assert len(cassette) == 1
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import yaml
|
|||||||
from vcr.cassette import Cassette
|
from vcr.cassette import Cassette
|
||||||
from vcr.errors import UnhandledHTTPRequestError
|
from vcr.errors import UnhandledHTTPRequestError
|
||||||
from vcr.patch import force_reset
|
from vcr.patch import force_reset
|
||||||
|
from vcr.request import Request
|
||||||
from vcr.stubs import VCRHTTPSConnection
|
from vcr.stubs import VCRHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
@@ -410,3 +411,25 @@ def test_find_requests_with_most_matches_many_similar_requests(mock_get_matchers
|
|||||||
(1, ["method", "path"], [("query", "failed : query")]),
|
(1, ["method", "path"], [("query", "failed : query")]),
|
||||||
(3, ["method", "path"], [("query", "failed : query")]),
|
(3, ["method", "path"], [("query", "failed : query")]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_used_interactions(tmpdir):
|
||||||
|
interactions = [
|
||||||
|
{"request": {"body": "", "uri": "foo1", "method": "GET", "headers": {}}, "response": "bar1"},
|
||||||
|
{"request": {"body": "", "uri": "foo2", "method": "GET", "headers": {}}, "response": "bar2"},
|
||||||
|
{"request": {"body": "", "uri": "foo3", "method": "GET", "headers": {}}, "response": "bar3"},
|
||||||
|
]
|
||||||
|
file = tmpdir.join("test_cassette.yml")
|
||||||
|
file.write(yaml.dump({"interactions": [interactions[0], interactions[1]]}))
|
||||||
|
|
||||||
|
cassette = Cassette.load(path=str(file))
|
||||||
|
request = Request._from_dict(interactions[1]["request"])
|
||||||
|
cassette.play_response(request)
|
||||||
|
assert len(cassette._played_interactions) < len(cassette._old_interactions)
|
||||||
|
|
||||||
|
request = Request._from_dict(interactions[2]["request"])
|
||||||
|
cassette.append(request, interactions[2]["response"])
|
||||||
|
assert len(cassette._new_interactions()) == 1
|
||||||
|
|
||||||
|
used_interactions = cassette._played_interactions + cassette._new_interactions()
|
||||||
|
assert len(used_interactions) == 2
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ class Cassette:
|
|||||||
custom_patches=(),
|
custom_patches=(),
|
||||||
inject=False,
|
inject=False,
|
||||||
allow_playback_repeats=False,
|
allow_playback_repeats=False,
|
||||||
|
drop_unused_requests=False,
|
||||||
):
|
):
|
||||||
self._persister = persister or FilesystemPersister
|
self._persister = persister or FilesystemPersister
|
||||||
self._path = path
|
self._path = path
|
||||||
@@ -189,6 +190,7 @@ class Cassette:
|
|||||||
self.record_mode = record_mode
|
self.record_mode = record_mode
|
||||||
self.custom_patches = custom_patches
|
self.custom_patches = custom_patches
|
||||||
self.allow_playback_repeats = allow_playback_repeats
|
self.allow_playback_repeats = allow_playback_repeats
|
||||||
|
self.drop_unused_requests = drop_unused_requests
|
||||||
|
|
||||||
# self.data is the list of (req, resp) tuples
|
# self.data is the list of (req, resp) tuples
|
||||||
self.data = []
|
self.data = []
|
||||||
@@ -196,6 +198,10 @@ class Cassette:
|
|||||||
self.dirty = False
|
self.dirty = False
|
||||||
self.rewound = False
|
self.rewound = False
|
||||||
|
|
||||||
|
# Subsets of self.data to store old and played interactions
|
||||||
|
self._old_interactions = []
|
||||||
|
self._played_interactions = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def play_count(self):
|
def play_count(self):
|
||||||
return sum(self.play_counts.values())
|
return sum(self.play_counts.values())
|
||||||
@@ -257,6 +263,7 @@ class Cassette:
|
|||||||
for index, response in self._responses(request):
|
for index, response in self._responses(request):
|
||||||
if self.play_counts[index] == 0 or self.allow_playback_repeats:
|
if self.play_counts[index] == 0 or self.allow_playback_repeats:
|
||||||
self.play_counts[index] += 1
|
self.play_counts[index] += 1
|
||||||
|
self._played_interactions.append((request, response))
|
||||||
return response
|
return response
|
||||||
# The cassette doesn't contain the request asked for.
|
# The cassette doesn't contain the request asked for.
|
||||||
raise UnhandledHTTPRequestError(
|
raise UnhandledHTTPRequestError(
|
||||||
@@ -317,12 +324,36 @@ class Cassette:
|
|||||||
|
|
||||||
return final_best_matches
|
return final_best_matches
|
||||||
|
|
||||||
|
def _new_interactions(self):
|
||||||
|
"""List of new HTTP interactions (request/response tuples)"""
|
||||||
|
new_interactions = []
|
||||||
|
for request, response in self.data:
|
||||||
|
if all(
|
||||||
|
not requests_match(request, old_request, self._match_on)
|
||||||
|
for old_request, _ in self._old_interactions
|
||||||
|
):
|
||||||
|
new_interactions.append((request, response))
|
||||||
|
return new_interactions
|
||||||
|
|
||||||
def _as_dict(self):
|
def _as_dict(self):
|
||||||
return {"requests": self.requests, "responses": self.responses}
|
return {"requests": self.requests, "responses": self.responses}
|
||||||
|
|
||||||
|
def _build_used_interactions_dict(self):
|
||||||
|
interactions = self._played_interactions + self._new_interactions()
|
||||||
|
cassete_dict = {
|
||||||
|
"requests": [request for request, _ in interactions],
|
||||||
|
"responses": [response for _, response in interactions],
|
||||||
|
}
|
||||||
|
return cassete_dict
|
||||||
|
|
||||||
def _save(self, force=False):
|
def _save(self, force=False):
|
||||||
|
if self.drop_unused_requests and len(self._played_interactions) < len(self._old_interactions):
|
||||||
|
cassete_dict = self._build_used_interactions_dict()
|
||||||
|
force = True
|
||||||
|
else:
|
||||||
|
cassete_dict = self._as_dict()
|
||||||
if force or self.dirty:
|
if force or self.dirty:
|
||||||
self._persister.save_cassette(self._path, self._as_dict(), serializer=self._serializer)
|
self._persister.save_cassette(self._path, cassete_dict, serializer=self._serializer)
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
@@ -330,6 +361,7 @@ class Cassette:
|
|||||||
requests, responses = self._persister.load_cassette(self._path, serializer=self._serializer)
|
requests, responses = self._persister.load_cassette(self._path, serializer=self._serializer)
|
||||||
for request, response in zip(requests, responses):
|
for request, response in zip(requests, responses):
|
||||||
self.append(request, response)
|
self.append(request, response)
|
||||||
|
self._old_interactions.append((request, response))
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
self.rewound = True
|
self.rewound = True
|
||||||
except (CassetteDecodeError, CassetteNotFoundError):
|
except (CassetteDecodeError, CassetteNotFoundError):
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class VCR:
|
|||||||
func_path_generator=None,
|
func_path_generator=None,
|
||||||
decode_compressed_response=False,
|
decode_compressed_response=False,
|
||||||
record_on_exception=True,
|
record_on_exception=True,
|
||||||
|
drop_unused_requests=False,
|
||||||
):
|
):
|
||||||
self.serializer = serializer
|
self.serializer = serializer
|
||||||
self.match_on = match_on
|
self.match_on = match_on
|
||||||
@@ -81,6 +82,7 @@ class VCR:
|
|||||||
self.decode_compressed_response = decode_compressed_response
|
self.decode_compressed_response = decode_compressed_response
|
||||||
self.record_on_exception = record_on_exception
|
self.record_on_exception = record_on_exception
|
||||||
self._custom_patches = tuple(custom_patches)
|
self._custom_patches = tuple(custom_patches)
|
||||||
|
self.drop_unused_requests = drop_unused_requests
|
||||||
|
|
||||||
def _get_serializer(self, serializer_name):
|
def _get_serializer(self, serializer_name):
|
||||||
try:
|
try:
|
||||||
@@ -151,6 +153,7 @@ class VCR:
|
|||||||
"func_path_generator": func_path_generator,
|
"func_path_generator": func_path_generator,
|
||||||
"allow_playback_repeats": kwargs.get("allow_playback_repeats", False),
|
"allow_playback_repeats": kwargs.get("allow_playback_repeats", False),
|
||||||
"record_on_exception": record_on_exception,
|
"record_on_exception": record_on_exception,
|
||||||
|
"drop_unused_requests": kwargs.get("drop_unused_requests", self.drop_unused_requests),
|
||||||
}
|
}
|
||||||
path = kwargs.get("path")
|
path = kwargs.get("path")
|
||||||
if path:
|
if path:
|
||||||
|
|||||||
Reference in New Issue
Block a user