1
0
mirror of https://github.com/kevin1024/vcrpy.git synced 2025-12-09 01:03:24 +00:00

Compare commits

..

49 Commits

Author SHA1 Message Date
Luiz Menezes
4ce937978e Add pytest-xdist 2018-05-06 19:23:56 -03:00
Luiz Menezes
f890709a20 Merge pull request #346 from carsonyl/patch-1
Convert extras_require conditional deps to PEP 508 form.
2018-05-06 19:06:06 -03:00
Carson Lam
d0ae5fa40b Merge branch 'master' of github.com:kevin1024/vcrpy into patch-1 2018-05-05 19:53:42 -07:00
Luiz Menezes
1562bc7659 Merge pull request #350 from lamby/895269-vcrpy-please-make-the-build-reproducible
Make the build reproducible
2018-05-03 12:31:29 -03:00
Luiz Menezes
16b69aa2e5 Merge pull request #320 from allisson/master
Update aiohttp_stub to work with binary content
2018-05-03 12:30:41 -03:00
Allisson Azevedo
d9caff107d Merge remote-tracking branch 'upstream/master' 2018-05-02 15:03:01 -03:00
Chris Lamb
f317490eec Make the build reproducible
Whilst working on the Reproducible Builds effort [0], we noticed
that vcrpy could not be built reproducibly.

This is due to the documentation including the absolute build path
via Python default arguments.

This was originally filed in Debian as #895269 [1].

 [0] https://reproducible-builds.org/
 [1] https://bugs.debian.org/895269

Signed-off-by: Chris Lamb <lamby@debian.org>
2018-05-02 10:14:43 -07:00
Luiz Menezes
cf13805973 Merge pull request #354 from kevin1024/build-quicker
Fix broken tests + build quicker
2018-05-02 13:31:36 -03:00
Luiz Menezes
389cb4d6e3 Temporarily pins aiohttp to version <3 2018-05-02 12:19:55 -03:00
Luiz Menezes
7a82d70391 Pin flask version (as latests 1.0 breaks retrocompatibility) 2018-05-02 11:59:01 -03:00
Carson Lam
f3b9966a2a Pass flake8. 2018-02-21 23:16:29 -08:00
Carson Lam
5ba1c7fbb6 Convert extras_require conditional deps to PEP 508 form. 2018-02-21 21:40:02 -08:00
Allisson Azevedo
ad153bd733 Merge remote-tracking branch 'upstream/master' 2018-02-17 11:51:04 -03:00
Hugo
42b3b16fe1 Remove boto 2018-01-19 23:56:06 +02:00
Hugo
531dc02ca5 Only test on single recent version of dependencies 2018-01-17 10:28:36 +02:00
Thomas Grainger
2156adb841 Merge pull request #340 from hugovk/rm-2.6
Drop support for EOL Python 2.6 and 3.3
2018-01-16 17:23:12 +00:00
Hugo
6caf7e962e Requests 1.x only supports Python <= 3.3 2018-01-15 22:37:27 +02:00
Hugo
97fbd7e0bd Test flakes on Python 2.7 (and 3.6) not pypy3.5 2018-01-15 22:36:19 +02:00
Hugo
ead48b1907 xfail for test_post_chunked_binary_secure on CPython 3.5 2018-01-15 15:05:53 +02:00
Hugo
1af4b2587e Add pypy3.5-5.9.0 2018-01-15 10:04:25 +02:00
Hugo
82fa50c092 Drop pypy3 2018-01-15 09:54:39 +02:00
Hugo
58d8980cfa Remove dependency on bugs.python.org/xmlrpc: 301 Moved Permanently 2018-01-15 09:54:11 +02:00
Hugo
c111ebab0a Remove dependency on old Flask 2018-01-15 09:54:11 +02:00
Hugo
943a15a967 Drop support for EOL Python 3.3 2018-01-15 09:54:11 +02:00
Hugo
d0aa6bcc8d Rewrite unnecessary list/tuple literals as set literals 2018-01-15 09:54:11 +02:00
Hugo
04fd730a08 Replace unnecessary list comprehension - 'all' can take a generator 2018-01-15 09:54:11 +02:00
Hugo
6156271c48 Automatic formatters supported in Python 2.7+ 2018-01-15 09:54:11 +02:00
Hugo
87666ba2e4 Multiple context managers supported in 2.7+ 2018-01-15 09:54:11 +02:00
Hugo
7915d07aff Update classifiers 2018-01-15 09:54:11 +02:00
Hugo
095e272191 Add python_requires to help pip install correct version 2018-01-15 09:54:11 +02:00
Hugo
42762ec806 collections.Counter is new in Python 2.7 2018-01-15 09:54:11 +02:00
Hugo
bfb38af8e1 Drop support for EOL Python 2.6 2018-01-15 09:54:11 +02:00
Hugo
894695d13b Stop testing on EOL Python 2.6 2018-01-15 09:54:11 +02:00
Andrew Kofink
a56a0726d4 Correct libyaml-devel package name in EL/Fedora 2017-10-30 10:20:56 -10:00
Ivan Malison
c366852925 Merge pull request #324 from petr-bulusek/custom-persister
Fix of registering custom persister and test.
2017-10-25 02:17:08 -07:00
Allisson Azevedo
0cab15658f Merge remote-tracking branch 'upstream/master' 2017-09-20 11:58:07 -03:00
Ivan Malison
c3ecf8c5b2 Merge pull request #326 from lmazuel/master
Fix mixup between httplib and urllib3
2017-08-17 10:08:38 -07:00
Laurent Mazuel
81d453f7d3 Add requests 2.18.4 to build 2017-08-17 08:37:11 -07:00
Laurent Mazuel
262ad903cb Don't unmock requests.packages if not necessary 2017-08-17 08:37:11 -07:00
Laurent Mazuel
ec60af0214 Fix mixup between httplib and urllib3 2017-08-17 08:37:11 -07:00
Petr Bulusek
8cf8d3f69c delete ipdb.set_trace 2017-08-12 17:58:52 +02:00
Petr Bulusek
034aeb4f17 typo 2017-08-11 14:41:11 +02:00
Petr Bulusek
d59efbc6e0 Instantiating class not necessary. 2017-08-09 15:18:00 +02:00
Petr Bulusek
b753a491c9 revert PR unrelated change, better comment 2017-08-09 14:44:39 +02:00
Petr Bulusek
9092b34dd1 register custom persister fix 2017-08-09 14:25:10 +02:00
Mikaeil Orfanian
0a3aaddca2 Update usage.rst
"i.e." is misleading because it means "in other words". In the context of this paragraph, "i.e." implies that "unexpected requests" are those that have a different URI.
I think a URI change is just one example of what could constitute an "unexpected" request. vcr does request matching based on method, port, body, etc.
So, I think "e.g." which means "for example" is the right phrase in this context.
2017-07-13 17:09:34 -05:00
Allisson Azevedo
c55d976277 Update aiohttp_stub to work with binary content 2017-06-22 15:12:58 -03:00
Ivan Malison
47ccddafee Merge pull request #314 from kevin1024/support-requests211
support requests 2.11 Fixes #313
2017-06-07 11:27:26 -07:00
Thomas Grainger
dcaf813657 support requests 2.11 Fixes #313 2017-06-07 14:10:23 +01:00
25 changed files with 196 additions and 171 deletions

View File

@@ -7,33 +7,17 @@ env:
- secure: LBSEg/gMj4u4Hrpo3zs6Y/1mTpd2RtcN49mZIFgTdbJ9IhpiNPqcEt647Lz94F9Eses2x2WbNuKqZKZZReY7QLbEzU1m0nN5jlaKrjcG5NR5clNABfFFyhgc0jBikyS4abAG8jc2efeaTrFuQwdoF4sE8YiVrkiVj2X5Xoi6sBk=
matrix:
- TOX_SUFFIX="flakes"
- TOX_SUFFIX="requests22"
- TOX_SUFFIX="requests23"
- TOX_SUFFIX="requests24"
- TOX_SUFFIX="requests25"
- TOX_SUFFIX="requests26"
- TOX_SUFFIX="requests27"
- TOX_SUFFIX="requests213"
- TOX_SUFFIX="requests216"
- TOX_SUFFIX="requests1"
- TOX_SUFFIX="httplib2"
- TOX_SUFFIX="boto"
- TOX_SUFFIX="boto3"
- TOX_SUFFIX="urllib319"
- TOX_SUFFIX="urllib3110"
- TOX_SUFFIX="urllib3121"
- TOX_SUFFIX="tornado3"
- TOX_SUFFIX="tornado4"
- TOX_SUFFIX="aiohttp"
matrix:
allow_failures:
- env: TOX_SUFFIX="boto"
- env: TOX_SUFFIX="boto3"
exclude:
- env: TOX_SUFFIX="flakes"
python: 2.6
- env: TOX_SUFFIX="flakes"
python: 2.7
# Only run flakes on a single Python 2.x and a single 3.x
- env: TOX_SUFFIX="flakes"
python: 3.4
- env: TOX_SUFFIX="flakes"
@@ -41,30 +25,17 @@ matrix:
- env: TOX_SUFFIX="flakes"
python: pypy
- env: TOX_SUFFIX="flakes"
python: pypy3
- env: TOX_SUFFIX="boto"
python: 3.6
- env: TOX_SUFFIX="requests1"
python: 3.4
- env: TOX_SUFFIX="requests1"
python: 3.5
- env: TOX_SUFFIX="requests1"
python: 3.6
- env: TOX_SUFFIX="aiohttp"
python: 2.6
python: "pypy3.5-5.9.0"
- env: TOX_SUFFIX="aiohttp"
python: 2.7
- env: TOX_SUFFIX="aiohttp"
python: pypy
- env: TOX_SUFFIX="aiohttp"
python: pypy3
python:
- 2.6
- 2.7
- 3.5
- 3.6
- pypy
- pypy3
- "pypy3.5-5.9.0"
install:
- pip install tox-travis
- if [[ $TOX_SUFFIX != 'flakes' ]]; then python setup.py install ; fi

View File

@@ -1,4 +1,4 @@
|PyPI| |Build Status| |Waffle Ready| |Gitter|
|PyPI| |Python versions| |Build Status| |Waffle Ready| |Gitter|
VCR.py
======
@@ -51,7 +51,6 @@ VCR.py works great with the following HTTP clients:
- urllib3
- tornado
- urllib2
- boto
- boto3
@@ -63,6 +62,8 @@ more details
.. |PyPI| image:: https://img.shields.io/pypi/v/vcrpy.svg
:target: https://pypi.python.org/pypi/vcrpy-unittest
.. |Python versions| image:: https://img.shields.io/pypi/pyversions/vcrpy-unittest.svg
:target: https://pypi.python.org/pypi/vcrpy-unittest
.. |Build Status| image:: https://secure.travis-ci.org/kevin1024/vcrpy.png?branch=master
:target: http://travis-ci.org/kevin1024/vcrpy
.. |Waffle Ready| image:: https://badge.waffle.io/kevin1024/vcrpy.png?label=ready&title=waffle

View File

@@ -9,7 +9,7 @@ with pip::
Compatibility
-------------
VCR.py supports Python 2.6 and 2.7, 3.3, 3.4, and
VCR.py supports Python 2.7 and 3.4+, and
`pypy <http://pypy.org>`__.
The following http libraries are supported:
@@ -40,7 +40,7 @@ rebuilding pyyaml.
brew install libyaml # Mac with Homebrew
apt-get install libyaml-dev # Ubuntu
dnf install libyaml-dev # Fedora
dnf install libyaml-devel # Fedora
3. Rebuild pyyaml with libyaml::

View File

@@ -57,7 +57,7 @@ once
file.
It is similar to the new\_episodes record mode, but will prevent new,
unexpected requests from being made (i.e. because the request URI
unexpected requests from being made (e.g. because the request URI
changed).
once is the default record mode, used when you do not set one.

View File

@@ -1,11 +1,9 @@
#!/usr/bin/env python
import sys
import logging
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
import pkg_resources
long_description = open('README.rst', 'r').read()
@@ -24,31 +22,14 @@ class PyTest(TestCommand):
sys.exit(errno)
install_requires = ['PyYAML', 'wrapt', 'six>=1.5']
extras_require = {
':python_version in "2.4, 2.5, 2.6"':
['contextlib2', 'backport_collections', 'mock'],
':python_version in "2.7, 3.1, 3.2"': ['contextlib2', 'mock'],
':python_version in "3.4, 3.5, 3.6"': ['yarl'],
}
try:
if 'bdist_wheel' not in sys.argv:
for key, value in extras_require.items():
if key.startswith(':') and pkg_resources.evaluate_marker(key[1:]):
install_requires.extend(value)
except Exception:
logging.getLogger(__name__).exception(
'Something went wrong calculating platform specific dependencies, so '
"you're getting them all!"
)
for key, value in extras_require.items():
if key.startswith(':'):
install_requires.extend(value)
install_requires = [
'PyYAML',
'wrapt',
'six>=1.5',
'contextlib2; python_version=="2.7"',
'mock; python_version=="2.7"',
'yarl; python_version>="3.4"',
]
excluded_packages = ["tests*"]
if sys.version_info[0] == 2:
@@ -66,8 +47,8 @@ setup(
author_email='me@kevinmccarthy.org',
url='https://github.com/kevin1024/vcrpy',
packages=find_packages(exclude=excluded_packages),
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
install_requires=install_requires,
extras_require=extras_require,
license='MIT',
tests_require=['pytest', 'mock', 'pytest-httpbin'],
classifiers=[
@@ -75,7 +56,14 @@ setup(
'Environment :: Console',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Testing',
'Topic :: Internet :: WWW/HTTP',
'License :: OSI Approved :: MIT License',

View File

@@ -3,11 +3,13 @@ import aiohttp
@asyncio.coroutine
def aiohttp_request(loop, method, url, as_text, **kwargs):
def aiohttp_request(loop, method, url, output='text', **kwargs):
with aiohttp.ClientSession(loop=loop) as session:
response = yield from session.request(method, url, **kwargs) # NOQA: E999
if as_text:
if output == 'text':
content = yield from response.text() # NOQA: E999
else:
elif output == 'json':
content = yield from response.json() # NOQA: E999
elif output == 'raw':
content = yield from response.read() # NOQA: E999
return response, content

View File

@@ -22,19 +22,19 @@ def run_in_loop(fn):
return loop.run_until_complete(task)
def request(method, url, as_text=True, **kwargs):
def request(method, url, output='text', **kwargs):
def run(loop):
return aiohttp_request(loop, method, url, as_text, **kwargs)
return aiohttp_request(loop, method, url, output=output, **kwargs)
return run_in_loop(run)
def get(url, as_text=True, **kwargs):
return request('GET', url, as_text, **kwargs)
def get(url, output='text', **kwargs):
return request('GET', url, output=output, **kwargs)
def post(url, as_text=True, **kwargs):
return request('POST', url, as_text, **kwargs)
def post(url, output='text', **kwargs):
return request('POST', url, output='text', **kwargs)
@pytest.fixture(params=["https", "http"])
@@ -79,14 +79,25 @@ def test_text(tmpdir, scheme):
def test_json(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
with vcr.use_cassette(str(tmpdir.join('json.yaml'))):
_, response_json = get(url, as_text=False)
_, response_json = get(url, output='json')
with vcr.use_cassette(str(tmpdir.join('json.yaml'))) as cassette:
_, cassette_response_json = get(url, as_text=False)
_, cassette_response_json = get(url, output='json')
assert cassette_response_json == response_json
assert cassette.play_count == 1
def test_binary(tmpdir, scheme):
url = scheme + '://httpbin.org/image/png'
with vcr.use_cassette(str(tmpdir.join('binary.yaml'))):
_, response_binary = get(url, output='raw')
with vcr.use_cassette(str(tmpdir.join('binary.yaml'))) as cassette:
_, cassette_response_binary = get(url, output='raw')
assert cassette_response_binary == response_binary
assert cassette.play_count == 1
def test_post(tmpdir, scheme):
data = {'key1': 'value1', 'key2': 'value2'}
url = scheme + '://httpbin.org/post'
@@ -103,10 +114,10 @@ def test_params(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
params = {'a': 1, 'b': False, 'c': 'c'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, as_text=False, params=params)
_, response_json = get(url, output='json', params=params)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, cassette_response_json = get(url, as_text=False, params=params)
_, cassette_response_json = get(url, output='json', params=params)
assert cassette_response_json == response_json
assert cassette.play_count == 1
@@ -115,15 +126,15 @@ def test_params_same_url_distinct_params(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
params = {'a': 1, 'b': False, 'c': 'c'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, as_text=False, params=params)
_, response_json = get(url, output='json', params=params)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, cassette_response_json = get(url, as_text=False, params=params)
_, cassette_response_json = get(url, output='json', params=params)
assert cassette_response_json == response_json
assert cassette.play_count == 1
other_params = {'other': 'params'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
response, cassette_response_text = get(url, as_text=True, params=other_params)
response, cassette_response_text = get(url, output='text', params=other_params)
assert 'No match for the request' in cassette_response_text
assert response.status == 599

View File

@@ -26,9 +26,9 @@ def test_ignore_localhost(tmpdir, httpbin):
with overridden_dns({'httpbin.org': '127.0.0.1'}):
cass_file = str(tmpdir.join('filter_qs.yaml'))
with vcr.use_cassette(cass_file, ignore_localhost=True) as cass:
urlopen('http://localhost:{0}/'.format(httpbin.port))
urlopen('http://localhost:{}/'.format(httpbin.port))
assert len(cass) == 0
urlopen('http://httpbin.org:{0}/'.format(httpbin.port))
urlopen('http://httpbin.org:{}/'.format(httpbin.port))
assert len(cass) == 1
@@ -39,9 +39,9 @@ def test_ignore_httpbin(tmpdir, httpbin):
cass_file,
ignore_hosts=['httpbin.org']
) as cass:
urlopen('http://httpbin.org:{0}/'.format(httpbin.port))
urlopen('http://httpbin.org:{}/'.format(httpbin.port))
assert len(cass) == 0
urlopen('http://localhost:{0}/'.format(httpbin.port))
urlopen('http://localhost:{}/'.format(httpbin.port))
assert len(cass) == 1
@@ -53,8 +53,8 @@ def test_ignore_localhost_and_httpbin(tmpdir, httpbin):
ignore_hosts=['httpbin.org'],
ignore_localhost=True
) as cass:
urlopen('http://httpbin.org:{0}'.format(httpbin.port))
urlopen('http://localhost:{0}'.format(httpbin.port))
urlopen('http://httpbin.org:{}'.format(httpbin.port))
urlopen('http://localhost:{}'.format(httpbin.port))
assert len(cass) == 0
@@ -62,12 +62,12 @@ def test_ignore_localhost_twice(tmpdir, httpbin):
with overridden_dns({'httpbin.org': '127.0.0.1'}):
cass_file = str(tmpdir.join('filter_qs.yaml'))
with vcr.use_cassette(cass_file, ignore_localhost=True) as cass:
urlopen('http://localhost:{0}'.format(httpbin.port))
urlopen('http://localhost:{}'.format(httpbin.port))
assert len(cass) == 0
urlopen('http://httpbin.org:{0}'.format(httpbin.port))
urlopen('http://httpbin.org:{}'.format(httpbin.port))
assert len(cass) == 1
with vcr.use_cassette(cass_file, ignore_localhost=True) as cass:
assert len(cass) == 1
urlopen('http://localhost:{0}'.format(httpbin.port))
urlopen('http://httpbin.org:{0}'.format(httpbin.port))
urlopen('http://localhost:{}'.format(httpbin.port))
urlopen('http://httpbin.org:{}'.format(httpbin.port))
assert len(cass) == 1

View File

@@ -10,10 +10,25 @@ import vcr
from vcr.persisters.filesystem import FilesystemPersister
class CustomFilesystemPersister(object):
'''Behaves just like default FilesystemPersister but adds .test extension
to the cassette file'''
@staticmethod
def load_cassette(cassette_path, serializer):
cassette_path += '.test'
return FilesystemPersister.load_cassette(cassette_path, serializer)
@staticmethod
def save_cassette(cassette_path, cassette_dict, serializer):
cassette_path += '.test'
FilesystemPersister.save_cassette(cassette_path, cassette_dict,
serializer)
def test_save_cassette_with_custom_persister(tmpdir, httpbin):
'''Ensure you can save a cassette using custom persister'''
my_vcr = vcr.VCR()
my_vcr.register_persister(FilesystemPersister)
my_vcr.register_persister(CustomFilesystemPersister)
# Check to make sure directory doesnt exist
assert not os.path.exists(str(tmpdir.join('nonexistent')))
@@ -23,7 +38,7 @@ def test_save_cassette_with_custom_persister(tmpdir, httpbin):
urlopen(httpbin.url).read()
# Callback should have made the file and the directory
assert os.path.exists(str(tmpdir.join('nonexistent', 'cassette.yml')))
assert os.path.exists(str(tmpdir.join('nonexistent', 'cassette.yml.test')))
def test_load_cassette_with_custom_persister(tmpdir, httpbin):
@@ -31,9 +46,9 @@ def test_load_cassette_with_custom_persister(tmpdir, httpbin):
Ensure you can load a cassette using custom persister
'''
my_vcr = vcr.VCR()
my_vcr.register_persister(FilesystemPersister)
my_vcr.register_persister(CustomFilesystemPersister)
test_fixture = str(tmpdir.join('synopsis.json'))
test_fixture = str(tmpdir.join('synopsis.json.test'))
with my_vcr.use_cassette(test_fixture, serializer='json'):
response = urlopen(httpbin.url).read()

View File

@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
'''Test requests' interaction with vcr'''
import platform
import pytest
import sys
import vcr
from assertions import assert_cassette_empty, assert_is_json
@@ -115,6 +117,9 @@ def test_post_chunked_binary(tmpdir, httpbin):
@pytest.mark.xfail('sys.version_info >= (3, 6)', strict=True, raises=ConnectionError)
@pytest.mark.xfail((3, 5) < sys.version_info < (3, 6) and
platform.python_implementation() == 'CPython',
reason='Fails on CPython 3.5')
def test_post_chunked_binary_secure(tmpdir, httpbin_secure):
'''Ensure that we can send chunked binary without breaking while trying to concatenate bytes with str.'''
data1 = iter([b'data', b'to', b'send'])
@@ -249,10 +254,8 @@ def test_nested_cassettes_with_session_created_before_nesting(httpbin_both, tmpd
def test_post_file(tmpdir, httpbin_both):
'''Ensure that we handle posting a file.'''
url = httpbin_both + '/post'
with vcr.use_cassette(str(tmpdir.join('post_file.yaml'))) as cass:
# Don't use 2.7+ only style ',' separated with here because we support python 2.6
with open('tox.ini') as f:
original_response = requests.post(url, f).content
with vcr.use_cassette(str(tmpdir.join('post_file.yaml'))) as cass, open('tox.ini') as f:
original_response = requests.post(url, f).content
# This also tests that we do the right thing with matching the body when they are files.
with vcr.use_cassette(str(tmpdir.join('post_file.yaml')),

View File

@@ -5,6 +5,7 @@
import pytest
import pytest_httpbin
import vcr
from vcr.patch import force_reset
from assertions import assert_cassette_empty, assert_is_json
urllib3 = pytest.importorskip("urllib3")
@@ -55,7 +56,7 @@ def test_body(tmpdir, httpbin_both, verify_pool_mgr):
def test_auth(tmpdir, httpbin_both, verify_pool_mgr):
'''Ensure that we can handle basic auth'''
auth = ('user', 'passwd')
headers = urllib3.util.make_headers(basic_auth='{0}:{1}'.format(*auth))
headers = urllib3.util.make_headers(basic_auth='{}:{}'.format(*auth))
url = httpbin_both.url + '/basic-auth/user/passwd'
with vcr.use_cassette(str(tmpdir.join('auth.yaml'))):
one = verify_pool_mgr.request('GET', url, headers=headers)
@@ -69,7 +70,7 @@ def test_auth(tmpdir, httpbin_both, verify_pool_mgr):
def test_auth_failed(tmpdir, httpbin_both, verify_pool_mgr):
'''Ensure that we can save failed auth statuses'''
auth = ('user', 'wrongwrongwrong')
headers = urllib3.util.make_headers(basic_auth='{0}:{1}'.format(*auth))
headers = urllib3.util.make_headers(basic_auth='{}:{}'.format(*auth))
url = httpbin_both.url + '/basic-auth/user/passwd'
with vcr.use_cassette(str(tmpdir.join('auth-failed.yaml'))) as cass:
# Ensure that this is empty to begin with
@@ -138,3 +139,21 @@ def test_gzip(tmpdir, httpbin_both, verify_pool_mgr):
def test_https_with_cert_validation_disabled(tmpdir, httpbin_secure, pool_mgr):
with vcr.use_cassette(str(tmpdir.join('cert_validation_disabled.yaml'))):
pool_mgr.request('GET', httpbin_secure.url)
def test_urllib3_force_reset():
cpool = urllib3.connectionpool
http_original = cpool.HTTPConnection
https_original = cpool.HTTPSConnection
verified_https_original = cpool.VerifiedHTTPSConnection
with vcr.use_cassette(path='test'):
first_cassette_HTTPConnection = cpool.HTTPConnection
first_cassette_HTTPSConnection = cpool.HTTPSConnection
first_cassette_VerifiedHTTPSConnection = cpool.VerifiedHTTPSConnection
with force_reset():
assert cpool.HTTPConnection is http_original
assert cpool.HTTPSConnection is https_original
assert cpool.VerifiedHTTPSConnection is verified_https_original
assert cpool.HTTPConnection is first_cassette_HTTPConnection
assert cpool.HTTPSConnection is first_cassette_HTTPSConnection
assert cpool.VerifiedHTTPSConnection is first_cassette_VerifiedHTTPSConnection

View File

@@ -1,5 +1,6 @@
import multiprocessing
import pytest
from six.moves import xmlrpc_client
from six.moves import xmlrpc_client, xmlrpc_server
requests = pytest.importorskip("requests")
@@ -80,13 +81,27 @@ def test_amazon_doctype(tmpdir):
assert 'html' in r.text
def test_xmlrpclib(tmpdir):
@pytest.yield_fixture(scope='session')
def rpc_server():
httpd = xmlrpc_server.SimpleXMLRPCServer(('', 0))
httpd.register_function(pow)
proxy_process = multiprocessing.Process(
target=httpd.serve_forever,
)
try:
proxy_process.start()
yield 'http://{}:{}'.format(*httpd.server_address)
finally:
proxy_process.terminate()
def test_xmlrpclib(tmpdir, rpc_server):
with vcr.use_cassette(str(tmpdir.join('xmlrpcvideo.yaml'))):
roundup_server = xmlrpc_client.ServerProxy('http://bugs.python.org/xmlrpc', allow_none=True)
original_schema = roundup_server.schema()
roundup_server = xmlrpc_client.ServerProxy(rpc_server, allow_none=True)
original_schema = roundup_server.pow(2, 4)
with vcr.use_cassette(str(tmpdir.join('xmlrpcvideo.yaml'))):
roundup_server = xmlrpc_client.ServerProxy('http://bugs.python.org/xmlrpc', allow_none=True)
second_schema = roundup_server.schema()
roundup_server = xmlrpc_client.ServerProxy(rpc_server, allow_none=True)
second_schema = roundup_server.pow(2, 4)
assert original_schema == second_schema

View File

@@ -22,7 +22,7 @@ def assert_matcher(matcher_name):
matcher = getattr(matchers, matcher_name)
for k1, k2 in itertools.permutations(REQUESTS, 2):
matched = matcher(REQUESTS[k1], REQUESTS[k2])
if matcher_name in set((k1, k2)):
if matcher_name in {k1, k2}:
assert not matched
else:
assert matched
@@ -31,7 +31,7 @@ def assert_matcher(matcher_name):
def test_uri_matcher():
for k1, k2 in itertools.permutations(REQUESTS, 2):
matched = matchers.uri(REQUESTS[k1], REQUESTS[k2])
if set((k1, k2)) != set(('base', 'method')):
if {k1, k2} != {'base', 'method'}:
assert not matched
else:
assert matched

View File

@@ -319,11 +319,11 @@ def test_additional_matchers():
@vcr.use_cassette
def function_defaults(cassette):
assert set(cassette._match_on) == set([vcr.matchers['uri']])
assert set(cassette._match_on) == {vcr.matchers['uri']}
@vcr.use_cassette(additional_matchers=('body',))
def function_additional(cassette):
assert set(cassette._match_on) == set([vcr.matchers['uri'], vcr.matchers['body']])
assert set(cassette._match_on) == {vcr.matchers['uri'], vcr.matchers['body']}
function_defaults()
function_additional()

30
tox.ini
View File

@@ -1,5 +1,5 @@
[tox]
envlist = {py26,py27,py35,py36,pypy,pypy3}-{flakes,requests216,requests213,requests27,requests26,requests25,requests24,requests23,requests22,requests1,httplib2,urllib319,urllib3110,urllib3121,tornado3,tornado4,boto,boto3,aiohttp}
envlist = {py27,py35,py36,pypy}-{flakes,requests27,httplib2,urllib3121,tornado4,boto3,aiohttp}
[testenv:flakes]
skipsdist = True
@@ -11,36 +11,22 @@ deps = flake8
[testenv]
commands =
./runtests.sh {posargs}
./runtests.sh -n 4 {posargs}
deps =
# httpbin fails with latest Flask, so we pin it
Flask==0.10.1
Flask<1
mock
pytest
pytest-httpbin
pytest-xdist
PyYAML
requests1: requests==1.2.3
requests216: requests==2.16.3
requests213: requests==2.13.0
requests27: requests==2.7.0
requests26: requests==2.6.0
requests25: requests==2.5.0
requests24: requests==2.4.0
requests23: requests==2.3.0
requests22: requests==2.2.1
httplib2: httplib2
urllib319: urllib3==1.9.1
urllib3110: urllib3==1.10.2
urllib3121: urllib3==1.21.1
{py26,py27,py35,py36,pypy}-tornado3: tornado>=3,<4
{py26,py27,py35,py36,pypy}-tornado4: tornado>=4,<5
{py26,py27,py35,py36,pypy}-tornado3: pytest-tornado
{py26,py27,py35,py36,pypy}-tornado4: pytest-tornado
{py26,py27,py35,py36}-tornado3: pycurl
{py26,py27,py35,py36}-tornado4: pycurl
boto: boto
{py27,py35,py36,pypy}-tornado4: tornado>=4,<5
{py27,py35,py36,pypy}-tornado4: pytest-tornado
{py27,py35,py36}-tornado4: pycurl
boto3: boto3
aiohttp: aiohttp
aiohttp: aiohttp<3
aiohttp: pytest-asyncio
[flake8]

View File

@@ -1,10 +1,11 @@
import collections
import sys
import inspect
import logging
import wrapt
from .compat import contextlib, collections
from .compat import contextlib
from .errors import UnhandledHTTPRequestError
from .matchers import requests_match, uri, method
from .patch import CassettePatcherBuilder
@@ -174,13 +175,13 @@ class Cassette(object):
def use(cls, **kwargs):
return CassetteContextDecorator.from_args(cls, **kwargs)
def __init__(self, path, serializer=yamlserializer, persister=FilesystemPersister, record_mode='once',
def __init__(self, path, serializer=None, persister=None, record_mode='once',
match_on=(uri, method), before_record_request=None,
before_record_response=None, custom_patches=(),
inject=False):
self._persister = persister
self._persister = persister or FilesystemPersister
self._path = path
self._serializer = serializer
self._serializer = serializer or yamlserializer
self._match_on = match_on
self._before_record_request = before_record_request or (lambda x: x)
self._before_record_response = before_record_response or (lambda x: x)
@@ -303,7 +304,7 @@ class Cassette(object):
pass
def __str__(self):
return "<Cassette containing {0} recorded response(s)>".format(
return "<Cassette containing {} recorded response(s)>".format(
len(self)
)

View File

@@ -11,8 +11,4 @@ else:
if not hasattr(contextlib, 'ExitStack'):
import contextlib2 as contextlib
import collections
if not hasattr(collections, 'Counter'):
import backport_collections as collections
__all__ = ['mock', 'contextlib', 'collections']
__all__ = ['mock', 'contextlib']

View File

@@ -1,4 +1,5 @@
import copy
import collections
import functools
import inspect
import os
@@ -6,7 +7,6 @@ import types
import six
from .compat import collections
from .cassette import Cassette
from .serializers import yamlserializer, jsonserializer
from .persisters.filesystem import FilesystemPersister
@@ -78,7 +78,7 @@ class VCR(object):
serializer = self.serializers[serializer_name]
except KeyError:
raise KeyError(
"Serializer {0} doesn't exist or isn't registered".format(
"Serializer {} doesn't exist or isn't registered".format(
serializer_name
)
)
@@ -91,7 +91,7 @@ class VCR(object):
matchers.append(self.matchers[m])
except KeyError:
raise KeyError(
"Matcher {0} doesn't exist or isn't registered".format(m)
"Matcher {} doesn't exist or isn't registered".format(m)
)
return matchers
@@ -145,6 +145,7 @@ class VCR(object):
merged_config = {
'serializer': self._get_serializer(serializer_name),
'persister': self.persister,
'match_on': self._get_matchers(
tuple(matcher_names) + tuple(additional_matchers)
),

View File

@@ -90,12 +90,12 @@ def _log_matches(r1, r2, matches):
differences = [m for m in matches if not m[0]]
if differences:
log.debug(
"Requests {0} and {1} differ according to "
"the following matchers: {2}".format(r1, r2, differences)
"Requests {} and {} differ according to "
"the following matchers: {}".format(r1, r2, differences)
)
def requests_match(r1, r2, matchers):
matches = [(m(r1, r2), m) for m in matchers]
_log_matches(r1, r2, matches)
return all([m[0] for m in matches])
return all(m[0] for m in matches)

View File

@@ -59,7 +59,7 @@ def build_uri(**parts):
port = parts['port']
scheme = parts['protocol']
default_port = {'https': 443, 'http': 80}[scheme]
parts['port'] = ':{0}'.format(port) if port != default_port else ''
parts['port'] = ':{}'.format(port) if port != default_port else ''
return "{protocol}://{host}{port}{path}".format(**parts)
@@ -161,7 +161,7 @@ def main():
for file_path in files:
migrated = try_migrate(file_path)
status = 'OK' if migrated else 'FAIL'
sys.stderr.write("[{0}] {1}\n".format(status, file_path))
sys.stderr.write("[{}] {}\n".format(status, file_path))
sys.stderr.write("Done.\n")

View File

@@ -169,7 +169,7 @@ class CassettePatcherBuilder(object):
bases = (base_class,)
if not issubclass(base_class, object): # Check for old style class
bases += (object,)
return type('{0}{1}'.format(base_class.__name__, self._cassette._path),
return type('{}{}'.format(base_class.__name__, self._cassette._path),
bases, dict(cassette=self._cassette))
@_build_patchers_from_mock_triples_decorator
@@ -362,8 +362,22 @@ class ConnectionRemover(object):
def reset_patchers():
yield mock.patch.object(httplib, 'HTTPConnection', _HTTPConnection)
yield mock.patch.object(httplib, 'HTTPSConnection', _HTTPSConnection)
try:
import requests.packages.urllib3.connectionpool as cpool
import requests
if requests.__build__ < 0x021603:
# Avoid double unmock if requests 2.16.3
# First, this is pointless, requests.packages.urllib3 *IS* urllib3 (see packages.py)
# Second, this is unmocking twice the same classes with different namespaces
# and is creating weird issues and bugs:
# > AssertionError: assert <class 'urllib3.connection.HTTPConnection'>
# > is <class 'requests.packages.urllib3.connection.HTTPConnection'>
# This assert should work!!!
# Note that this also means that now, requests.packages is never imported
# if requests 2.16.3 or greater is used with VCRPy.
import requests.packages.urllib3.connectionpool as cpool
else:
raise ImportError("Skip requests not vendored anymore")
except ImportError: # pragma: no cover
pass
else:
@@ -386,11 +400,11 @@ def reset_patchers():
pass
else:
yield mock.patch.object(cpool, 'VerifiedHTTPSConnection', _VerifiedHTTPSConnection)
yield mock.patch.object(cpool, 'HTTPConnection', _HTTPConnection)
yield mock.patch.object(cpool, 'HTTPSConnection', _HTTPSConnection)
yield mock.patch.object(cpool, 'HTTPConnection', _cpoolHTTPConnection)
yield mock.patch.object(cpool, 'HTTPSConnection', _cpoolHTTPSConnection)
if hasattr(cpool.HTTPConnectionPool, 'ConnectionCls'):
yield mock.patch.object(cpool.HTTPConnectionPool, 'ConnectionCls', _HTTPConnection)
yield mock.patch.object(cpool.HTTPSConnectionPool, 'ConnectionCls', _HTTPSConnection)
yield mock.patch.object(cpool.HTTPConnectionPool, 'ConnectionCls', _cpoolHTTPConnection)
yield mock.patch.object(cpool.HTTPSConnectionPool, 'ConnectionCls', _cpoolHTTPSConnection)
try:
import botocore.vendored.requests.packages.urllib3.connectionpool as cpool

View File

@@ -81,7 +81,7 @@ class Request(object):
return self.scheme
def __str__(self):
return "<Request ({0}) {1}>".format(self.method, self.uri)
return "<Request ({}) {}>".format(self.method, self.uri)
def __repr__(self):
return self.__str__()

View File

@@ -132,11 +132,11 @@ class VCRConnection(object):
"""
port = self.real_connection.port
default_port = {'https': 443, 'http': 80}[self._protocol]
return ':{0}'.format(port) if port != default_port else ''
return ':{}'.format(port) if port != default_port else ''
def _uri(self, url):
"""Returns request absolute URI"""
uri = "{0}://{1}{2}{3}".format(
uri = "{}://{}{}{}".format(
self._protocol,
self.real_connection.host,
self._port_postfix(),
@@ -146,7 +146,7 @@ class VCRConnection(object):
def _url(self, uri):
"""Returns request selector url from absolute URI"""
prefix = "{0}://{1}{2}".format(
prefix = "{}://{}{}".format(
self._protocol,
self.real_connection.host,
self._port_postfix(),
@@ -161,7 +161,7 @@ class VCRConnection(object):
body=body,
headers=headers or {}
)
log.debug('Got {0}'.format(self._vcr_request))
log.debug('Got {}'.format(self._vcr_request))
# Note: The request may not actually be finished at this point, so
# I'm not sending the actual request until getresponse(). This
@@ -180,7 +180,7 @@ class VCRConnection(object):
body="",
headers={}
)
log.debug('Got {0}'.format(self._vcr_request))
log.debug('Got {}'.format(self._vcr_request))
def putheader(self, header, *values):
self._vcr_request.headers[header] = values
@@ -214,7 +214,7 @@ class VCRConnection(object):
# then return it
if self.cassette.can_play_response_for(self._vcr_request):
log.info(
"Playing response for {0} from cassette".format(
"Playing response for {} from cassette".format(
self._vcr_request
)
)
@@ -236,7 +236,7 @@ class VCRConnection(object):
# and return it.
log.info(
"{0} not in cassette, sending to real server".format(
"{} not in cassette, sending to real server".format(
self._vcr_request
)
)

View File

@@ -21,6 +21,10 @@ class MockClientResponse(ClientResponse):
def text(self, encoding='utf-8'):
return self.content.decode(encoding)
@asyncio.coroutine
def read(self):
return self.content
@asyncio.coroutine
def release(self):
pass
@@ -71,7 +75,7 @@ def vcr_request(cassette, real_request):
'message': response.reason,
},
'headers': dict(response.headers),
'body': {'string': (yield from response.text())}, # NOQA: E999
'body': {'string': (yield from response.read())}, # NOQA: E999
'url': response.url,
}
cassette.append(vcr_request, vcr_response)

View File

@@ -13,9 +13,7 @@ class VCRHTTPConnectionWithTimeout(VCRHTTPConnection,
HTTPConnection.__init__.'''
# Delete the keyword arguments that HTTPConnection would not recognize
safe_keys = set(
('host', 'port', 'strict', 'timeout', 'source_address')
)
safe_keys = {'host', 'port', 'strict', 'timeout', 'source_address'}
unknown_keys = set(kwargs.keys()) - safe_keys
safe_kwargs = kwargs.copy()
for kw in unknown_keys:
@@ -33,7 +31,7 @@ class VCRHTTPSConnectionWithTimeout(VCRHTTPSConnection,
def __init__(self, *args, **kwargs):
# Delete the keyword arguments that HTTPSConnection would not recognize
safe_keys = set((
safe_keys = {
'host',
'port',
'key_file',
@@ -42,7 +40,7 @@ class VCRHTTPSConnectionWithTimeout(VCRHTTPSConnection,
'timeout',
'source_address',
'ca_certs',
))
}
unknown_keys = set(kwargs.keys()) - safe_keys
safe_kwargs = kwargs.copy()
for kw in unknown_keys: