mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-09 09:13:23 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0aa5fddb7 | ||
|
|
e54aeadc68 | ||
|
|
c4a33d1cff |
@@ -608,6 +608,8 @@ new API in version 1.0.x
|
|||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
---------
|
---------
|
||||||
|
- 1.7.1 [#183] Patch ``fetch_impl`` instead of the entire HTTPClient
|
||||||
|
class for Tornado (thanks @abhinav).
|
||||||
- 1.7.0 [#177] Properly support coroutine/generator decoration. [#178]
|
- 1.7.0 [#177] Properly support coroutine/generator decoration. [#178]
|
||||||
Support distribute (thanks @graingert). [#163] Make compatibility
|
Support distribute (thanks @graingert). [#163] Make compatibility
|
||||||
between python2 and python3 recorded cassettes more robust (thanks
|
between python2 and python3 recorded cassettes more robust (thanks
|
||||||
|
|||||||
6
setup.py
6
setup.py
@@ -9,6 +9,7 @@ import pkg_resources
|
|||||||
|
|
||||||
long_description = open('README.rst', 'r').read()
|
long_description = open('README.rst', 'r').read()
|
||||||
|
|
||||||
|
|
||||||
class PyTest(TestCommand):
|
class PyTest(TestCommand):
|
||||||
|
|
||||||
def finalize_options(self):
|
def finalize_options(self):
|
||||||
@@ -23,7 +24,7 @@ class PyTest(TestCommand):
|
|||||||
sys.exit(errno)
|
sys.exit(errno)
|
||||||
|
|
||||||
|
|
||||||
install_requires=['PyYAML', 'wrapt', 'six>=1.5']
|
install_requires = ['PyYAML', 'wrapt', 'six>=1.5']
|
||||||
|
|
||||||
|
|
||||||
extras_require = {
|
extras_require = {
|
||||||
@@ -47,9 +48,10 @@ except Exception:
|
|||||||
if key.startswith(':'):
|
if key.startswith(':'):
|
||||||
install_requires.extend(value)
|
install_requires.extend(value)
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='vcrpy',
|
name='vcrpy',
|
||||||
version='1.7.0',
|
version='1.7.1',
|
||||||
description=(
|
description=(
|
||||||
"Automatically mock your HTTP interactions to simplify and "
|
"Automatically mock your HTTP interactions to simplify and "
|
||||||
"speed up testing"
|
"speed up testing"
|
||||||
|
|||||||
@@ -298,3 +298,31 @@ def test_tornado_exception_can_be_caught(get_client):
|
|||||||
yield get(get_client(), 'http://httpbin.org/status/404')
|
yield get(get_client(), 'http://httpbin.org/status/404')
|
||||||
except http.HTTPError as e:
|
except http.HTTPError as e:
|
||||||
assert e.code == 404
|
assert e.code == 404
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
def test_existing_references_get_patched(tmpdir):
|
||||||
|
from tornado.httpclient import AsyncHTTPClient
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('data.yaml'))):
|
||||||
|
client = AsyncHTTPClient()
|
||||||
|
yield get(client, 'http://httpbin.org/get')
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('data.yaml'))) as cass:
|
||||||
|
yield get(client, 'http://httpbin.org/get')
|
||||||
|
assert cass.play_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.gen_test
|
||||||
|
def test_existing_instances_get_patched(get_client, tmpdir):
|
||||||
|
'''Ensure that existing instances of AsyncHTTPClient get patched upon
|
||||||
|
entering VCR context.'''
|
||||||
|
|
||||||
|
client = get_client()
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('data.yaml'))):
|
||||||
|
yield get(client, 'http://httpbin.org/get')
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('data.yaml'))) as cass:
|
||||||
|
yield get(client, 'http://httpbin.org/get')
|
||||||
|
assert cass.play_count == 1
|
||||||
|
|||||||
40
vcr/patch.py
40
vcr/patch.py
@@ -54,13 +54,12 @@ else:
|
|||||||
|
|
||||||
# Try to save the original types for Tornado
|
# Try to save the original types for Tornado
|
||||||
try:
|
try:
|
||||||
import tornado.httpclient
|
|
||||||
import tornado.simple_httpclient
|
import tornado.simple_httpclient
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
_AsyncHTTPClient = tornado.httpclient.AsyncHTTPClient
|
_SimpleAsyncHTTPClient_fetch_impl = \
|
||||||
_SimpleAsyncHTTPClient = tornado.simple_httpclient.SimpleAsyncHTTPClient
|
tornado.simple_httpclient.SimpleAsyncHTTPClient.fetch_impl
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -68,7 +67,8 @@ try:
|
|||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
_CurlAsyncHTTPClient = tornado.curl_httpclient.CurlAsyncHTTPClient
|
_CurlAsyncHTTPClient_fetch_impl = \
|
||||||
|
tornado.curl_httpclient.CurlAsyncHTTPClient.fetch_impl
|
||||||
|
|
||||||
|
|
||||||
class CassettePatcherBuilder(object):
|
class CassettePatcherBuilder(object):
|
||||||
@@ -228,23 +228,27 @@ class CassettePatcherBuilder(object):
|
|||||||
@_build_patchers_from_mock_triples_decorator
|
@_build_patchers_from_mock_triples_decorator
|
||||||
def _tornado(self):
|
def _tornado(self):
|
||||||
try:
|
try:
|
||||||
import tornado.httpclient as http
|
|
||||||
import tornado.simple_httpclient as simple
|
import tornado.simple_httpclient as simple
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
from .stubs.tornado_stubs import VCRAsyncHTTPClient
|
from .stubs.tornado_stubs import vcr_fetch_impl
|
||||||
from .stubs.tornado_stubs import VCRSimpleAsyncHTTPClient
|
|
||||||
|
|
||||||
yield http, 'AsyncHTTPClient', VCRAsyncHTTPClient
|
new_fetch_impl = vcr_fetch_impl(
|
||||||
yield simple, 'SimpleAsyncHTTPClient', VCRSimpleAsyncHTTPClient
|
self._cassette, _SimpleAsyncHTTPClient_fetch_impl
|
||||||
|
)
|
||||||
|
yield simple.SimpleAsyncHTTPClient, 'fetch_impl', new_fetch_impl
|
||||||
try:
|
try:
|
||||||
import tornado.curl_httpclient as curl
|
import tornado.curl_httpclient as curl
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
from .stubs.tornado_stubs import VCRCurlAsyncHTTPClient
|
from .stubs.tornado_stubs import vcr_fetch_impl
|
||||||
yield curl, 'CurlAsyncHTTPClient', VCRCurlAsyncHTTPClient
|
|
||||||
|
new_fetch_impl = vcr_fetch_impl(
|
||||||
|
self._cassette, _CurlAsyncHTTPClient_fetch_impl
|
||||||
|
)
|
||||||
|
yield curl.CurlAsyncHTTPClient, 'fetch_impl', new_fetch_impl
|
||||||
|
|
||||||
def _urllib3_patchers(self, cpool, stubs):
|
def _urllib3_patchers(self, cpool, stubs):
|
||||||
http_connection_remover = ConnectionRemover(
|
http_connection_remover = ConnectionRemover(
|
||||||
@@ -362,19 +366,25 @@ def reset_patchers():
|
|||||||
_CertValidatingHTTPSConnection)
|
_CertValidatingHTTPSConnection)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tornado.httpclient as http
|
|
||||||
import tornado.simple_httpclient as simple
|
import tornado.simple_httpclient as simple
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
yield mock.patch.object(http, 'AsyncHTTPClient', _AsyncHTTPClient)
|
yield mock.patch.object(
|
||||||
yield mock.patch.object(simple, 'SimpleAsyncHTTPClient', _SimpleAsyncHTTPClient)
|
simple.SimpleAsyncHTTPClient,
|
||||||
|
'fetch_impl',
|
||||||
|
_SimpleAsyncHTTPClient_fetch_impl,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
import tornado.curl_httpclient as curl
|
import tornado.curl_httpclient as curl
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
yield mock.patch.object(curl, 'CurlAsyncHTTPClient', _CurlAsyncHTTPClient)
|
yield mock.patch.object(
|
||||||
|
curl.CurlAsyncHTTPClient,
|
||||||
|
'fetch_impl',
|
||||||
|
_CurlAsyncHTTPClient_fetch_impl,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
|
|||||||
@@ -1,48 +1,20 @@
|
|||||||
'''Stubs for tornado HTTP clients'''
|
'''Stubs for tornado HTTP clients'''
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import functools
|
||||||
from six import BytesIO
|
from six import BytesIO
|
||||||
|
|
||||||
from tornado import httputil
|
from tornado import httputil
|
||||||
from tornado.httpclient import AsyncHTTPClient
|
|
||||||
from tornado.httpclient import HTTPResponse
|
from tornado.httpclient import HTTPResponse
|
||||||
from tornado.simple_httpclient import SimpleAsyncHTTPClient
|
|
||||||
|
|
||||||
from vcr.errors import CannotOverwriteExistingCassetteException
|
from vcr.errors import CannotOverwriteExistingCassetteException
|
||||||
from vcr.request import Request
|
from vcr.request import Request
|
||||||
|
|
||||||
|
|
||||||
class _VCRAsyncClient(object):
|
def vcr_fetch_impl(cassette, real_fetch_impl):
|
||||||
cassette = None
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
@functools.wraps(real_fetch_impl)
|
||||||
from vcr.patch import force_reset
|
def new_fetch_impl(self, request, callback):
|
||||||
with force_reset():
|
|
||||||
return super(_VCRAsyncClient, cls).__new__(cls, *args, **kwargs)
|
|
||||||
|
|
||||||
def initialize(self, *args, **kwargs):
|
|
||||||
from vcr.patch import force_reset
|
|
||||||
with force_reset():
|
|
||||||
self.real_client = self._baseclass(*args, **kwargs)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def io_loop(self):
|
|
||||||
return self.real_client.io_loop
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _closed(self):
|
|
||||||
return self.real_client._closed
|
|
||||||
|
|
||||||
@property
|
|
||||||
def defaults(self):
|
|
||||||
return self.real_client.defaults
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
from vcr.patch import force_reset
|
|
||||||
with force_reset():
|
|
||||||
self.real_client.close()
|
|
||||||
|
|
||||||
def fetch_impl(self, request, callback):
|
|
||||||
headers = dict(request.headers)
|
headers = dict(request.headers)
|
||||||
if request.user_agent:
|
if request.user_agent:
|
||||||
headers.setdefault('User-Agent', request.user_agent)
|
headers.setdefault('User-Agent', request.user_agent)
|
||||||
@@ -74,8 +46,8 @@ class _VCRAsyncClient(object):
|
|||||||
headers,
|
headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.cassette.can_play_response_for(vcr_request):
|
if cassette.can_play_response_for(vcr_request):
|
||||||
vcr_response = self.cassette.play_response(vcr_request)
|
vcr_response = cassette.play_response(vcr_request)
|
||||||
headers = httputil.HTTPHeaders()
|
headers = httputil.HTTPHeaders()
|
||||||
|
|
||||||
recorded_headers = vcr_response['headers']
|
recorded_headers = vcr_response['headers']
|
||||||
@@ -93,7 +65,7 @@ class _VCRAsyncClient(object):
|
|||||||
)
|
)
|
||||||
return callback(response)
|
return callback(response)
|
||||||
else:
|
else:
|
||||||
if self.cassette.write_protected and self.cassette.filter_request(
|
if cassette.write_protected and cassette.filter_request(
|
||||||
vcr_request
|
vcr_request
|
||||||
):
|
):
|
||||||
response = HTTPResponse(
|
response = HTTPResponse(
|
||||||
@@ -103,8 +75,7 @@ class _VCRAsyncClient(object):
|
|||||||
"No match for the request (%r) was found. "
|
"No match for the request (%r) was found. "
|
||||||
"Can't overwrite existing cassette (%r) in "
|
"Can't overwrite existing cassette (%r) in "
|
||||||
"your current record mode (%r)."
|
"your current record mode (%r)."
|
||||||
% (vcr_request, self.cassette._path,
|
% (vcr_request, cassette._path, cassette.record_mode)
|
||||||
self.cassette.record_mode)
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return callback(response)
|
return callback(response)
|
||||||
@@ -123,26 +94,9 @@ class _VCRAsyncClient(object):
|
|||||||
'headers': headers,
|
'headers': headers,
|
||||||
'body': {'string': response.body},
|
'body': {'string': response.body},
|
||||||
}
|
}
|
||||||
self.cassette.append(vcr_request, vcr_response)
|
cassette.append(vcr_request, vcr_response)
|
||||||
return callback(response)
|
return callback(response)
|
||||||
|
|
||||||
from vcr.patch import force_reset
|
real_fetch_impl(self, request, new_callback)
|
||||||
with force_reset():
|
|
||||||
self.real_client.fetch_impl(request, new_callback)
|
|
||||||
|
|
||||||
|
return new_fetch_impl
|
||||||
class VCRAsyncHTTPClient(_VCRAsyncClient, AsyncHTTPClient):
|
|
||||||
_baseclass = AsyncHTTPClient
|
|
||||||
|
|
||||||
|
|
||||||
class VCRSimpleAsyncHTTPClient(_VCRAsyncClient, SimpleAsyncHTTPClient):
|
|
||||||
_baseclass = SimpleAsyncHTTPClient
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from tornado.curl_httpclient import CurlAsyncHTTPClient
|
|
||||||
except ImportError: # pragma: no cover
|
|
||||||
VCRCurlAsyncHTTPClient = None
|
|
||||||
else:
|
|
||||||
class VCRCurlAsyncHTTPClient(_VCRAsyncClient, CurlAsyncHTTPClient):
|
|
||||||
_baseclass = CurlAsyncHTTPClient
|
|
||||||
|
|||||||
Reference in New Issue
Block a user