mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-09 01:03:24 +00:00
@@ -457,6 +457,8 @@ API in version 1.0.x
|
|||||||
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
* 1.2.0 Add custom_patches argument to VCR/Cassette objects to allow
|
||||||
|
users to stub custom classes when cassettes become active.
|
||||||
* 1.1.4 Add force reset around calls to actual connection from stubs, to ensure
|
* 1.1.4 Add force reset around calls to actual connection from stubs, to ensure
|
||||||
compatibility with the version of httplib/urlib2 in python 2.7.9.
|
compatibility with the version of httplib/urlib2 in python 2.7.9.
|
||||||
* 1.1.3 Fix python3 headers field (thanks @rtaboada), fix boto test (thanks
|
* 1.1.3 Fix python3 headers field (thanks @rtaboada), fix boto test (thanks
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
##!/usr/bin/env python
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
@@ -20,7 +20,7 @@ class PyTest(TestCommand):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='vcrpy',
|
name='vcrpy',
|
||||||
version='1.1.4',
|
version='1.2.0',
|
||||||
description=(
|
description=(
|
||||||
"Automatically mock your HTTP interactions to simplify and "
|
"Automatically mock your HTTP interactions to simplify and "
|
||||||
"speed up testing"
|
"speed up testing"
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import pytest
|
|||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from vcr.cassette import Cassette
|
from vcr.cassette import Cassette
|
||||||
from vcr.patch import force_reset
|
|
||||||
from vcr.errors import UnhandledHTTPRequestError
|
from vcr.errors import UnhandledHTTPRequestError
|
||||||
|
from vcr.patch import force_reset
|
||||||
|
from vcr.stubs import VCRHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_cassette_load(tmpdir):
|
def test_cassette_load(tmpdir):
|
||||||
@@ -181,3 +183,21 @@ def test_nesting_context_managers_by_checking_references_of_http_connection():
|
|||||||
assert httplib.HTTPConnection is original
|
assert httplib.HTTPConnection is original
|
||||||
assert httplib.HTTPConnection is second_cassette_HTTPConnection
|
assert httplib.HTTPConnection is second_cassette_HTTPConnection
|
||||||
assert httplib.HTTPConnection is first_cassette_HTTPConnection
|
assert httplib.HTTPConnection is first_cassette_HTTPConnection
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_patchers():
|
||||||
|
class Test(object):
|
||||||
|
attribute = None
|
||||||
|
with Cassette.use('custom_patches', custom_patches=((Test, 'attribute', VCRHTTPSConnection),)):
|
||||||
|
assert issubclass(Test.attribute, VCRHTTPSConnection)
|
||||||
|
assert VCRHTTPSConnection is not Test.attribute
|
||||||
|
old_attribute = Test.attribute
|
||||||
|
|
||||||
|
with Cassette.use('custom_patches', custom_patches=((Test, 'attribute', VCRHTTPSConnection),)):
|
||||||
|
assert issubclass(Test.attribute, VCRHTTPSConnection)
|
||||||
|
assert VCRHTTPSConnection is not Test.attribute
|
||||||
|
assert Test.attribute is not old_attribute
|
||||||
|
|
||||||
|
assert issubclass(Test.attribute, VCRHTTPSConnection)
|
||||||
|
assert VCRHTTPSConnection is not Test.attribute
|
||||||
|
assert Test.attribute is old_attribute
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import pytest
|
|||||||
|
|
||||||
from vcr import VCR, use_cassette
|
from vcr import VCR, use_cassette
|
||||||
from vcr.request import Request
|
from vcr.request import Request
|
||||||
|
from vcr.stubs import VCRHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
def test_vcr_use_cassette():
|
def test_vcr_use_cassette():
|
||||||
@@ -74,3 +75,18 @@ def test_fixtures_with_use_cassette(random_fixture):
|
|||||||
# fixtures. It is admittedly a bit strange because the test would never even
|
# fixtures. It is admittedly a bit strange because the test would never even
|
||||||
# run if the relevant feature were broken.
|
# run if the relevant feature were broken.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_patchers():
|
||||||
|
class Test(object):
|
||||||
|
attribute = None
|
||||||
|
attribute2 = None
|
||||||
|
test_vcr = VCR(custom_patches=((Test, 'attribute', VCRHTTPSConnection),))
|
||||||
|
with test_vcr.use_cassette('custom_patches'):
|
||||||
|
assert issubclass(Test.attribute, VCRHTTPSConnection)
|
||||||
|
assert VCRHTTPSConnection is not Test.attribute
|
||||||
|
|
||||||
|
with test_vcr.use_cassette('custom_patches', custom_patches=((Test, 'attribute2', VCRHTTPSConnection),)):
|
||||||
|
assert issubclass(Test.attribute, VCRHTTPSConnection)
|
||||||
|
assert VCRHTTPSConnection is not Test.attribute
|
||||||
|
assert Test.attribute is Test.attribute2
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class Cassette(object):
|
|||||||
match_on=(uri, method), filter_headers=(),
|
match_on=(uri, method), filter_headers=(),
|
||||||
filter_query_parameters=(), before_record_request=None,
|
filter_query_parameters=(), before_record_request=None,
|
||||||
before_record_response=None, ignore_hosts=(),
|
before_record_response=None, ignore_hosts=(),
|
||||||
ignore_localhost=()):
|
ignore_localhost=(), custom_patches=()):
|
||||||
self._path = path
|
self._path = path
|
||||||
self._serializer = serializer
|
self._serializer = serializer
|
||||||
self._match_on = match_on
|
self._match_on = match_on
|
||||||
@@ -100,6 +100,7 @@ class Cassette(object):
|
|||||||
self.dirty = False
|
self.dirty = False
|
||||||
self.rewound = False
|
self.rewound = False
|
||||||
self.record_mode = record_mode
|
self.record_mode = record_mode
|
||||||
|
self.custom_patches = custom_patches
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def play_count(self):
|
def play_count(self):
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from . import filters
|
|||||||
class VCR(object):
|
class VCR(object):
|
||||||
|
|
||||||
def __init__(self, serializer='yaml', cassette_library_dir=None,
|
def __init__(self, serializer='yaml', cassette_library_dir=None,
|
||||||
record_mode="once", filter_headers=(),
|
record_mode="once", filter_headers=(), custom_patches=(),
|
||||||
filter_query_parameters=(), before_record_request=None,
|
filter_query_parameters=(), before_record_request=None,
|
||||||
before_record_response=None, ignore_hosts=(),
|
before_record_response=None, ignore_hosts=(),
|
||||||
match_on=('method', 'scheme', 'host', 'port', 'path', 'query',),
|
match_on=('method', 'scheme', 'host', 'port', 'path', 'query',),
|
||||||
@@ -43,6 +43,7 @@ class VCR(object):
|
|||||||
self.before_record_response = before_record_response
|
self.before_record_response = before_record_response
|
||||||
self.ignore_hosts = ignore_hosts
|
self.ignore_hosts = ignore_hosts
|
||||||
self.ignore_localhost = ignore_localhost
|
self.ignore_localhost = ignore_localhost
|
||||||
|
self._custom_patches = tuple(custom_patches)
|
||||||
|
|
||||||
def _get_serializer(self, serializer_name):
|
def _get_serializer(self, serializer_name):
|
||||||
try:
|
try:
|
||||||
@@ -68,6 +69,9 @@ class VCR(object):
|
|||||||
def use_cassette(self, path, with_current_defaults=False, **kwargs):
|
def use_cassette(self, path, with_current_defaults=False, **kwargs):
|
||||||
if with_current_defaults:
|
if with_current_defaults:
|
||||||
return Cassette.use(path, self.get_path_and_merged_config(path, **kwargs))
|
return Cassette.use(path, self.get_path_and_merged_config(path, **kwargs))
|
||||||
|
# This is made a function that evaluates every time a cassette is made so that
|
||||||
|
# changes that are made to this VCR instance that occur AFTER the use_cassette
|
||||||
|
# decorator is applied still affect subsequent calls to the decorated function.
|
||||||
args_getter = functools.partial(self.get_path_and_merged_config, path, **kwargs)
|
args_getter = functools.partial(self.get_path_and_merged_config, path, **kwargs)
|
||||||
return Cassette.use_arg_getter(args_getter)
|
return Cassette.use_arg_getter(args_getter)
|
||||||
|
|
||||||
@@ -86,7 +90,8 @@ class VCR(object):
|
|||||||
'match_on': self._get_matchers(matcher_names),
|
'match_on': self._get_matchers(matcher_names),
|
||||||
'record_mode': kwargs.get('record_mode', self.record_mode),
|
'record_mode': kwargs.get('record_mode', self.record_mode),
|
||||||
'before_record_request': self._build_before_record_request(kwargs),
|
'before_record_request': self._build_before_record_request(kwargs),
|
||||||
'before_record_response': self._build_before_record_response(kwargs)
|
'before_record_response': self._build_before_record_response(kwargs),
|
||||||
|
'custom_patches': self._custom_patches + kwargs.get('custom_patches', ())
|
||||||
}
|
}
|
||||||
return path, merged_config
|
return path, merged_config
|
||||||
|
|
||||||
|
|||||||
20
vcr/patch.py
20
vcr/patch.py
@@ -69,9 +69,12 @@ class CassettePatcherBuilder(object):
|
|||||||
self._class_to_cassette_subclass = {}
|
self._class_to_cassette_subclass = {}
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return itertools.chain(self._httplib(), self._requests(),
|
return itertools.chain(
|
||||||
self._urllib3(), self._httplib2(),
|
self._httplib(), self._requests(), self._urllib3(), self._httplib2(),
|
||||||
self._boto())
|
self._boto(), self._build_patchers_from_mock_triples(
|
||||||
|
self._cassette.custom_patches
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _build_patchers_from_mock_triples(self, mock_triples):
|
def _build_patchers_from_mock_triples(self, mock_triples):
|
||||||
for args in mock_triples:
|
for args in mock_triples:
|
||||||
@@ -88,6 +91,17 @@ class CassettePatcherBuilder(object):
|
|||||||
replacement_class))
|
replacement_class))
|
||||||
|
|
||||||
def _recursively_apply_get_cassette_subclass(self, replacement_dict_or_obj):
|
def _recursively_apply_get_cassette_subclass(self, replacement_dict_or_obj):
|
||||||
|
"""One of the subtleties of this class is that it does not directly
|
||||||
|
replace HTTPSConnection with VCRRequestsHTTPSConnection, but a
|
||||||
|
subclass of this class that has cassette assigned to the
|
||||||
|
appropriate value. This behavior is necessary to properly
|
||||||
|
support nested cassette contexts
|
||||||
|
|
||||||
|
This function exists to ensure that we use the same class
|
||||||
|
object (reference) to patch everything that replaces
|
||||||
|
VCRRequestHTTP[S]Connection, but that we can talk about
|
||||||
|
patching them with the raw references instead.
|
||||||
|
"""
|
||||||
if isinstance(replacement_dict_or_obj, dict):
|
if isinstance(replacement_dict_or_obj, dict):
|
||||||
for key, replacement_obj in replacement_dict_or_obj.items():
|
for key, replacement_obj in replacement_dict_or_obj.items():
|
||||||
replacement_obj = self._recursively_apply_get_cassette_subclass(
|
replacement_obj = self._recursively_apply_get_cassette_subclass(
|
||||||
|
|||||||
Reference in New Issue
Block a user