From 042ee790e2f6ce5014f35d630603bc273d85ca77 Mon Sep 17 00:00:00 2001 From: Tyson Holub Date: Thu, 2 Jul 2020 10:10:49 -0400 Subject: [PATCH] add allow_playback_repeats option to Cassette --- docs/advanced.rst | 18 ++++++++++++++++++ tests/unit/test_cassettes.py | 25 +++++++++++++++++++++++++ vcr/cassette.py | 8 +++++--- vcr/config.py | 1 + 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/docs/advanced.rst b/docs/advanced.rst index 5536055..b16710b 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -33,6 +33,8 @@ consider part of the API. The fields are as follows: been played back. - ``responses_of(request)``: Access the responses that match a given request +- ``allow_playback_repeats``: A boolean indicating whether responses + can be played back more than once. The ``Request`` object has the following properties: @@ -386,3 +388,19 @@ VCR.py allows to rewind a cassette in order to replay it inside the same functio assert cass.all_played cass.rewind() assert not cass.all_played + +Playback Repeats +---------------- + +By default, each response in a cassette can only be matched and played back +once while the cassette is in use, unless the cassette is rewound. + +If you want to allow playback repeats without rewinding the cassette, use +the Cassette ``allow_playback_repeats`` option. + +.. code:: python + + with vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml', allow_playback_repeats=True) as cass: + for x in range(10): + response = urllib2.urlopen('http://www.zombo.com/').read() + assert cass.all_played diff --git a/tests/unit/test_cassettes.py b/tests/unit/test_cassettes.py index 90a05c7..adbc77a 100644 --- a/tests/unit/test_cassettes.py +++ b/tests/unit/test_cassettes.py @@ -137,6 +137,31 @@ def test_cassette_all_played(): assert a.all_played +@mock.patch("vcr.cassette.requests_match", _mock_requests_match) +def test_cassette_allow_playback_repeats(): + a = Cassette("test", allow_playback_repeats=True) + a.append("foo", "bar") + a.append("other", "resp") + for x in range(10): + assert a.play_response("foo") == "bar" + assert a.play_count == 10 + assert a.all_played is False + assert a.play_response("other") == "resp" + assert a.play_count == 11 + assert a.all_played + + a.allow_playback_repeats = False + with pytest.raises(UnhandledHTTPRequestError) as e: + a.play_response("foo") + assert str(e.value) == "\"The cassette ('test') doesn't contain the request ('foo') asked for\"" + a.rewind() + assert a.all_played is False + assert a.play_response("foo") == "bar" + assert a.all_played is False + assert a.play_response("other") == "resp" + assert a.all_played + + @mock.patch("vcr.cassette.requests_match", _mock_requests_match) def test_cassette_rewound(): a = Cassette("test") diff --git a/vcr/cassette.py b/vcr/cassette.py index 826a68b..9122b4a 100644 --- a/vcr/cassette.py +++ b/vcr/cassette.py @@ -182,6 +182,7 @@ class Cassette: before_record_response=None, custom_patches=(), inject=False, + allow_playback_repeats=False, ): self._persister = persister or FilesystemPersister self._path = path @@ -193,6 +194,7 @@ class Cassette: self.inject = inject self.record_mode = record_mode self.custom_patches = custom_patches + self.allow_playback_repeats = allow_playback_repeats # self.data is the list of (req, resp) tuples self.data = [] @@ -207,7 +209,7 @@ class Cassette: @property def all_played(self): """Returns True if all responses have been played, False otherwise.""" - return self.play_count == len(self) + return len(self.play_counts.values()) == len(self) @property def requests(self): @@ -259,7 +261,7 @@ class Cassette: 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] == 0 or self.allow_playback_repeats: self.play_counts[index] += 1 return response # The cassette doesn't contain the request asked for. @@ -349,6 +351,6 @@ class Cassette: def __contains__(self, request): """Return whether or not a request has been stored""" for index, response in self._responses(request): - if self.play_counts[index] == 0: + if self.play_counts[index] == 0 or self.allow_playback_repeats: return True return False diff --git a/vcr/config.py b/vcr/config.py index 7dfd1a8..e95908c 100644 --- a/vcr/config.py +++ b/vcr/config.py @@ -149,6 +149,7 @@ class VCR: "inject": kwargs.get("inject_cassette", self.inject_cassette), "path_transformer": path_transformer, "func_path_generator": func_path_generator, + "allow_playback_repeats": kwargs.get("allow_playback_repeats", False), } path = kwargs.get("path") if path: