From 643a4c91ee5e4a5c743c28af5b75681b73c99308 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Thu, 18 Sep 2014 02:52:44 -0700 Subject: [PATCH] Change use_cassette to pass a function to CassetteContextDecorator so that changes to the default settings on the vcr properly propogate. --- tests/unit/test_cassettes.py | 26 ++++++++++++++++++++++++++ tests/unit/test_vcr.py | 22 ++++++++++++++++++---- vcr/cassette.py | 20 +++++++++++++------- vcr/config.py | 9 ++++++--- 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/tests/unit/test_cassettes.py b/tests/unit/test_cassettes.py index 2589aa8..c0f415b 100644 --- a/tests/unit/test_cassettes.py +++ b/tests/unit/test_cassettes.py @@ -86,6 +86,32 @@ def test_function_decorated_with_use_cassette_can_be_invoked_multiple_times(*arg decorated_function() +def test_arg_getter_functionality(): + arg_getter = mock.Mock(return_value=('test', {})) + context_decorator = Cassette.use_arg_getter(arg_getter) + + with context_decorator as cassette: + assert cassette._path == 'test' + + arg_getter.return_value = ('other', {}) + + with context_decorator as cassette: + assert cassette._path == 'other' + + arg_getter.return_value = ('', {'filter_headers': ('header_name',)}) + + @context_decorator + def function(): + pass + + with mock.patch.object(Cassette, '__init__', return_value=None) as cassette_init, \ + mock.patch.object(Cassette, '_load'), \ + mock.patch.object(Cassette, '__exit__'): + function() + cassette_init.assert_called_once_with(arg_getter.return_value[0], + **arg_getter.return_value[1]) + + def test_cassette_not_all_played(): a = Cassette('test') a.append('foo', 'bar') diff --git a/tests/unit/test_vcr.py b/tests/unit/test_vcr.py index aaae969..d460bc2 100644 --- a/tests/unit/test_vcr.py +++ b/tests/unit/test_vcr.py @@ -3,14 +3,28 @@ import mock from vcr import VCR - def test_vcr_use_cassette(): filter_headers = mock.Mock() test_vcr = VCR(filter_headers=filter_headers) - with mock.patch('vcr.config.Cassette') as mock_cassette_class: + with mock.patch('vcr.cassette.Cassette.__init__', return_value=None) as mock_cassette_init, \ + mock.patch('vcr.cassette.Cassette._load'), \ + mock.patch('vcr.cassette.Cassette.__exit__'): @test_vcr.use_cassette('test') def function(): pass - mock_cassette_class.call_count == 0 + assert mock_cassette_init.call_count == 0 function() - assert mock_cassette_class.use.call_args[1]['filter_headers'] is filter_headers + assert mock_cassette_init.call_args[1]['filter_headers'] is filter_headers + + # Make sure that calls to function now use cassettes with the + # new filter_header_settings + test_vcr.filter_headers = ('a',) + function() + assert mock_cassette_init.call_args[1]['filter_headers'] == test_vcr.filter_headers + + # Ensure that explicitly provided arguments still supercede + # those on the vcr. + new_filter_headers = mock.Mock() + + with test_vcr.use_cassette('test', filter_headers=new_filter_headers) as cassette: + assert cassette._filter_headers == new_filter_headers diff --git a/vcr/cassette.py b/vcr/cassette.py index 4b10f18..cb46e35 100644 --- a/vcr/cassette.py +++ b/vcr/cassette.py @@ -24,13 +24,17 @@ class CassetteContextDecorator(object): from interfering with another. """ - def __init__(self, cls, *args, **kwargs): - self.args = args - self.kwargs = kwargs + @classmethod + def from_args(cls, cassette_class, path, **kwargs): + return cls(cassette_class, lambda: (path, kwargs)) + + def __init__(self, cls, args_getter): self.cls = cls + self._args_getter = args_getter def __enter__(self): - self._cassette = self.cls.load(*self.args, **self.kwargs) + path, kwargs = self._args_getter() + self._cassette = self.cls.load(path, **kwargs) return self._cassette.__enter__() def __exit__(self, *args): @@ -55,10 +59,12 @@ class Cassette(object): return new_cassette @classmethod - def use(cls, *args, **kwargs): - return CassetteContextDecorator(cls, *args, **kwargs) + def use_arg_getter(cls, arg_getter): + return CassetteContextDecorator(cls, arg_getter) - use_cassette = use + @classmethod + def use(cls, *args, **kwargs): + return CassetteContextDecorator.from_args(cls, *args, **kwargs) def __init__(self, path, diff --git a/vcr/config.py b/vcr/config.py index 0727372..8308b48 100644 --- a/vcr/config.py +++ b/vcr/config.py @@ -1,3 +1,4 @@ +import functools import os from .cassette import Cassette from .serializers import yamlserializer, jsonserializer @@ -74,13 +75,16 @@ class VCR(object): return matchers def use_cassette(self, path, **kwargs): + args_getter = functools.partial(self.get_path_and_merged_config, path, **kwargs) + return Cassette.use_arg_getter(args_getter) + + def get_path_and_merged_config(self, path, **kwargs): serializer_name = kwargs.get('serializer', self.serializer) matcher_names = kwargs.get('match_on', self.match_on) cassette_library_dir = kwargs.get( 'cassette_library_dir', self.cassette_library_dir ) - if cassette_library_dir: path = os.path.join(cassette_library_dir, path) @@ -107,8 +111,7 @@ class VCR(object): 'ignore_localhost', self.ignore_localhost ), } - - return Cassette.use(path, **merged_config) + return path, merged_config def register_serializer(self, name, serializer): self.serializers[name] = serializer