mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-09 01:03:24 +00:00
httplib2 support
This commit is contained in:
committed by
Kevin McCarthy
parent
6bb67567f9
commit
46a2c25f6a
140
tests/integration/test_httplib2.py
Normal file
140
tests/integration/test_httplib2.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
'''Integration tests with httplib2'''
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
# External imports
|
||||||
|
from urllib import urlencode
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Internal imports
|
||||||
|
import vcr
|
||||||
|
|
||||||
|
from assertions import assert_cassette_has_one_response
|
||||||
|
|
||||||
|
httplib2 = pytest.importorskip("httplib2")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=["https", "http"])
|
||||||
|
def scheme(request):
|
||||||
|
"""
|
||||||
|
Fixture that returns both http and https
|
||||||
|
"""
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
def test_response_code(scheme, tmpdir):
|
||||||
|
'''Ensure we can read a response code from a fetch'''
|
||||||
|
url = scheme + '://httpbin.org/'
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('atts.yaml'))) as cass:
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
code = resp.status
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('atts.yaml'))) as cass:
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
assert code == resp.status
|
||||||
|
|
||||||
|
|
||||||
|
def test_random_body(scheme, tmpdir):
|
||||||
|
'''Ensure we can read the content, and that it's served from cache'''
|
||||||
|
url = scheme + '://httpbin.org/bytes/1024'
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('body.yaml'))) as cass:
|
||||||
|
_, content = httplib2.Http().request(url)
|
||||||
|
body = content
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('body.yaml'))) as cass:
|
||||||
|
_, content = httplib2.Http().request(url)
|
||||||
|
assert body == content
|
||||||
|
|
||||||
|
|
||||||
|
def test_response_headers(scheme, tmpdir):
|
||||||
|
'''Ensure we can get information from the response'''
|
||||||
|
url = scheme + '://httpbin.org/'
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('headers.yaml'))) as cass:
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
headers = resp.items()
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('headers.yaml'))) as cass:
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
assert headers == resp.items()
|
||||||
|
|
||||||
|
|
||||||
|
def test_multiple_requests(scheme, tmpdir):
|
||||||
|
'''Ensure that we can cache multiple requests'''
|
||||||
|
urls = [
|
||||||
|
scheme + '://httpbin.org/',
|
||||||
|
scheme + '://httpbin.org/',
|
||||||
|
scheme + '://httpbin.org/get',
|
||||||
|
scheme + '://httpbin.org/bytes/1024'
|
||||||
|
]
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('multiple.yaml'))) as cass:
|
||||||
|
[httplib2.Http().request(url) for url in urls]
|
||||||
|
assert len(cass) == len(urls)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_data(scheme, tmpdir):
|
||||||
|
'''Ensure that it works with query data'''
|
||||||
|
data = urlencode({'some': 1, 'data': 'here'})
|
||||||
|
url = scheme + '://httpbin.org/get?' + data
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('get_data.yaml'))) as cass:
|
||||||
|
_, res1 = httplib2.Http().request(url)
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('get_data.yaml'))) as cass:
|
||||||
|
_, res2 = httplib2.Http().request(url)
|
||||||
|
|
||||||
|
assert res1 == res2
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_data(scheme, tmpdir):
|
||||||
|
'''Ensure that it works when posting data'''
|
||||||
|
data = urlencode({'some': 1, 'data': 'here'})
|
||||||
|
url = scheme + '://httpbin.org/post'
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('post_data.yaml'))) as cass:
|
||||||
|
_, res1 = httplib2.Http().request(url, "POST", data)
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('post_data.yaml'))) as cass:
|
||||||
|
_, res2 = httplib2.Http().request(url, "POST", data)
|
||||||
|
|
||||||
|
assert res1 == res2
|
||||||
|
assert_cassette_has_one_response(cass)
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_unicode_data(scheme, tmpdir):
|
||||||
|
'''Ensure that it works when posting unicode data'''
|
||||||
|
data = urlencode({'snowman': u'☃'.encode('utf-8')})
|
||||||
|
url = scheme + '://httpbin.org/post'
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('post_data.yaml'))) as cass:
|
||||||
|
_, res1 = httplib2.Http().request(url, "POST", data)
|
||||||
|
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('post_data.yaml'))) as cass:
|
||||||
|
_, res2 = httplib2.Http().request(url, "POST", data)
|
||||||
|
|
||||||
|
assert res1 == res2
|
||||||
|
assert_cassette_has_one_response(cass)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cross_scheme(tmpdir):
|
||||||
|
'''Ensure that requests between schemes are treated separately'''
|
||||||
|
# First fetch a url under https, and then again under https and then
|
||||||
|
# ensure that we haven't served anything out of cache, and we have two
|
||||||
|
# requests / response pairs in the cassette
|
||||||
|
with vcr.use_cassette(str(tmpdir.join('cross_scheme.yaml'))) as cass:
|
||||||
|
httplib2.Http().request('https://httpbin.org/')
|
||||||
|
httplib2.Http().request('http://httpbin.org/')
|
||||||
|
assert len(cass) == 2
|
||||||
|
assert cass.play_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_decorator(scheme, tmpdir):
|
||||||
|
'''Test the decorator version of VCR.py'''
|
||||||
|
url = scheme + '://httpbin.org/'
|
||||||
|
|
||||||
|
@vcr.use_cassette(str(tmpdir.join('atts.yaml')))
|
||||||
|
def inner1():
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
return resp['status']
|
||||||
|
|
||||||
|
@vcr.use_cassette(str(tmpdir.join('atts.yaml')))
|
||||||
|
def inner2():
|
||||||
|
resp, _ = httplib2.Http().request(url)
|
||||||
|
return resp['status']
|
||||||
|
|
||||||
|
assert inner1() == inner2()
|
||||||
24
tox.ini
24
tox.ini
@@ -4,7 +4,8 @@
|
|||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py26, py27, pypy, py26requests, py27requests, pypyrequests, py26oldrequests, py27oldrequests, pypyoldrequests
|
#envlist = py26, py27, pypy, py26requests, py27requests, pypyrequests, py26oldrequests, py27oldrequests, pypyoldrequests
|
||||||
|
envlist = py26, py27, pypy, py26httplib2, py27httplib2, pypyhttplib2
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands =
|
commands =
|
||||||
@@ -61,3 +62,24 @@ deps =
|
|||||||
pytest
|
pytest
|
||||||
PyYAML
|
PyYAML
|
||||||
requests
|
requests
|
||||||
|
|
||||||
|
[testenv:py26httplib2]
|
||||||
|
basepython = python2.6
|
||||||
|
deps =
|
||||||
|
pytest
|
||||||
|
PyYAML
|
||||||
|
httplib2
|
||||||
|
|
||||||
|
[testenv:py27httplib2]
|
||||||
|
basepython = python2.7
|
||||||
|
deps =
|
||||||
|
pytest
|
||||||
|
PyYAML
|
||||||
|
httplib2
|
||||||
|
|
||||||
|
[testenv:pypyhttplib2]
|
||||||
|
basepython = pypy
|
||||||
|
deps =
|
||||||
|
pytest
|
||||||
|
PyYAML
|
||||||
|
httplib2
|
||||||
|
|||||||
31
vcr/patch.py
31
vcr/patch.py
@@ -24,6 +24,15 @@ try:
|
|||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to save the original types for httplib2
|
||||||
|
import httplib2
|
||||||
|
_HTTPConnectionWithTimeout = httplib2.HTTPConnectionWithTimeout
|
||||||
|
_HTTPSConnectionWithTimeout = httplib2.HTTPSConnectionWithTimeout
|
||||||
|
_SCHEME_TO_CONNECTION = httplib2.SCHEME_TO_CONNECTION
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def install(cassette):
|
def install(cassette):
|
||||||
"""
|
"""
|
||||||
@@ -64,6 +73,20 @@ def install(cassette):
|
|||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# patch httplib2
|
||||||
|
try:
|
||||||
|
import httplib2 as cpool
|
||||||
|
from .stubs.httplib2_stubs import VCRHTTPConnectionWithTimeout
|
||||||
|
from .stubs.httplib2_stubs import VCRHTTPSConnectionWithTimeout
|
||||||
|
cpool.HTTPConnectionWithTimeout = VCRHTTPConnectionWithTimeout
|
||||||
|
cpool.HTTPSConnectionWithTimeout = VCRHTTPSConnectionWithTimeout
|
||||||
|
cpool.SCHEME_TO_CONNECTION = {
|
||||||
|
'http': VCRHTTPConnectionWithTimeout,
|
||||||
|
'https': VCRHTTPSConnectionWithTimeout
|
||||||
|
}
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
'''Undo all the patching'''
|
'''Undo all the patching'''
|
||||||
@@ -91,3 +114,11 @@ def reset():
|
|||||||
cpool.HTTPSConnectionPool.ConnectionCls = _HTTPSConnection
|
cpool.HTTPSConnectionPool.ConnectionCls = _HTTPSConnection
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import httplib2 as cpool
|
||||||
|
cpool.HTTPConnectionWithTimeout = _HTTPConnectionWithTimeout
|
||||||
|
cpool.HTTPSConnectionWithTimeout = _HTTPSConnectionWithTimeout
|
||||||
|
cpool.SCHEME_TO_CONNECTION = _SCHEME_TO_CONNECTION
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'''Stubs for patching HTTP and HTTPS requests'''
|
'''Stubs for patching HTTP and HTTPS requests'''
|
||||||
|
|
||||||
from httplib import HTTPConnection, HTTPSConnection, HTTPMessage
|
from httplib import HTTPConnection, HTTPSConnection, HTTPMessage, HTTPResponse
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
from vcr.request import Request
|
from vcr.request import Request
|
||||||
@@ -31,7 +31,7 @@ def parse_headers(header_list):
|
|||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
class VCRHTTPResponse(object):
|
class VCRHTTPResponse(HTTPResponse):
|
||||||
"""
|
"""
|
||||||
Stub reponse class that gets returned instead of a HTTPResponse
|
Stub reponse class that gets returned instead of a HTTPResponse
|
||||||
"""
|
"""
|
||||||
@@ -175,6 +175,28 @@ class VCRConnection:
|
|||||||
def set_debuglevel(self, *args, **kwargs):
|
def set_debuglevel(self, *args, **kwargs):
|
||||||
self.real_connection.set_debuglevel(*args, **kwargs)
|
self.real_connection.set_debuglevel(*args, **kwargs)
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""
|
||||||
|
httplib2 uses this. Connects to the server I'm assuming.
|
||||||
|
|
||||||
|
Only pass to the baseclass if we don't have a recorded response
|
||||||
|
and are not write-protected.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hasattr(self, '_vcr_request') and \
|
||||||
|
self._vcr_request in self.cassette and \
|
||||||
|
self.cassette.record_mode != "all" and \
|
||||||
|
self.cassette.rewound:
|
||||||
|
# We already have a response we are going to play, don't
|
||||||
|
# actually connect
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.cassette.write_protected:
|
||||||
|
# Cassette is write-protected, don't actually connect
|
||||||
|
return
|
||||||
|
|
||||||
|
return self.real_connection.connect(self)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# need to temporarily reset here because the real connection
|
# need to temporarily reset here because the real connection
|
||||||
# inherits from the thing that we are mocking out. Take out
|
# inherits from the thing that we are mocking out. Take out
|
||||||
|
|||||||
51
vcr/stubs/httplib2_stubs.py
Normal file
51
vcr/stubs/httplib2_stubs.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'''Stubs for httplib2'''
|
||||||
|
|
||||||
|
from httplib2 import HTTPConnectionWithTimeout, HTTPSConnectionWithTimeout
|
||||||
|
from ..stubs import VCRHTTPConnection, VCRHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
|
class VCRHTTPConnectionWithTimeout(VCRHTTPConnection,
|
||||||
|
HTTPConnectionWithTimeout):
|
||||||
|
_baseclass = HTTPConnectionWithTimeout
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
'''I overrode the init because I need to clean kwargs before calling
|
||||||
|
HTTPConnection.__init__.'''
|
||||||
|
|
||||||
|
# Delete the keyword arguments that HTTPConnection would not recognize
|
||||||
|
safe_keys = set(('host', 'port', 'strict', 'timeout', 'source_address'))
|
||||||
|
unknown_keys = set(kwargs.keys()) - safe_keys
|
||||||
|
safe_kwargs = kwargs.copy()
|
||||||
|
for kw in unknown_keys:
|
||||||
|
del safe_kwargs[kw]
|
||||||
|
|
||||||
|
self.proxy_info = kwargs.pop('proxy_info', None)
|
||||||
|
VCRHTTPConnection.__init__(self, *args, **safe_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class VCRHTTPSConnectionWithTimeout(VCRHTTPSConnection,
|
||||||
|
HTTPSConnectionWithTimeout):
|
||||||
|
_baseclass = HTTPSConnectionWithTimeout
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
# Delete the keyword arguments that HTTPSConnection would not recognize
|
||||||
|
safe_keys = set(('host', 'port', 'key_file', 'cert_file', 'strict',
|
||||||
|
'timeout', 'source_address'))
|
||||||
|
unknown_keys = set(kwargs.keys()) - safe_keys
|
||||||
|
safe_kwargs = kwargs.copy()
|
||||||
|
for kw in unknown_keys:
|
||||||
|
del safe_kwargs[kw]
|
||||||
|
self.proxy_info = kwargs.pop('proxy_info', None)
|
||||||
|
if not 'ca_certs' in kwargs or kwargs['ca_certs'] is None:
|
||||||
|
try:
|
||||||
|
import httplib2
|
||||||
|
self.ca_certs = httplib2.CA_CERTS
|
||||||
|
except ImportError:
|
||||||
|
self.ca_certs = None
|
||||||
|
else:
|
||||||
|
self.ca_certs = kwargs['ca_certs']
|
||||||
|
|
||||||
|
self.disable_ssl_certificate_validation = kwargs.pop(
|
||||||
|
'disable_ssl_certificate_validation', None)
|
||||||
|
VCRHTTPSConnection.__init__(self, *args, **safe_kwargs)
|
||||||
Reference in New Issue
Block a user