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

Compare commits

..

1 Commits

Author SHA1 Message Date
Luiz Menezes
4ce937978e Add pytest-xdist 2018-05-06 19:23:56 -03:00
26 changed files with 136 additions and 367 deletions

1
.gitignore vendored
View File

@@ -1,7 +1,6 @@
*.pyc
.tox
.cache
.pytest_cache/
build/
dist/
*.egg/

View File

@@ -14,37 +14,8 @@ env:
- TOX_SUFFIX="tornado4"
- TOX_SUFFIX="aiohttp"
matrix:
include:
- env: TOX_SUFFIX="flakes"
python: 3.7
dist: xenial
sudo: true
- env: TOX_SUFFIX="requests27"
python: 3.7
dist: xenial
sudo: true
- env: TOX_SUFFIX="httplib2"
python: 3.7
dist: xenial
sudo: true
- env: TOX_SUFFIX="urllib3121"
python: 3.7
dist: xenial
sudo: true
- env: TOX_SUFFIX="tornado4"
python: 3.7
dist: xenial
sudo: true
- env: TOX_SUFFIX="aiohttp"
python: 3.7
dist: xenial
sudo: true
allow_failures:
- env: TOX_SUFFIX="boto3"
- env: TOX_SUFFIX="aiohttp"
python: "pypy3.5-5.9.0"
- env: TOX_SUFFIX="aiohttp"
python: 3.4
exclude:
# Only run flakes on a single Python 2.x and a single 3.x
- env: TOX_SUFFIX="flakes"
@@ -61,7 +32,6 @@ matrix:
python: pypy
python:
- 2.7
- 3.4
- 3.5
- 3.6
- pypy

View File

@@ -41,6 +41,19 @@ VCR.py will detect the absence of a cassette file and once again record
all HTTP interactions, which will update them to correspond to the new
API.
Support
-------
VCR.py works great with the following HTTP clients:
- requests
- aiohttp
- urllib3
- tornado
- urllib2
- boto3
License
=======
@@ -48,12 +61,12 @@ This library uses the MIT license. See `LICENSE.txt <LICENSE.txt>`__ for
more details
.. |PyPI| image:: https://img.shields.io/pypi/v/vcrpy.svg
:target: https://pypi.python.org/pypi/vcrpy
.. |Python versions| image:: https://img.shields.io/pypi/pyversions/vcrpy.svg
:target: https://pypi.python.org/pypi/vcrpy
.. |Build Status| image:: https://secure.travis-ci.org/kevin1024/vcrpy.svg?branch=master
: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.svg?label=ready&title=waffle
.. |Waffle Ready| image:: https://badge.waffle.io/kevin1024/vcrpy.png?label=ready&title=waffle
:target: https://waffle.io/kevin1024/vcrpy
.. |Gitter| image:: https://badges.gitter.im/Join%20Chat.svg
:alt: Join the chat at https://gitter.im/kevin1024/vcrpy

View File

@@ -221,25 +221,24 @@ Custom Request filtering
~~~~~~~~~~~~~~~~~~~~~~~~
If none of these covers your request filtering needs, you can register a
callback with the ``before_record_request`` configuration option to
manipulate the HTTP request before adding it to the cassette, or return
``None`` to ignore it entirely. Here is an example that will never record
requests to the ``'/login'`` path:
callback that will manipulate the HTTP request before adding it to the
cassette. Use the ``before_record_request`` configuration option to so this.
Here is an example that will never record requests to the /login
endpoint.
.. code:: python
def before_record_cb(request):
if request.path == '/login':
return None
return request
if request.path != '/login':
return request
my_vcr = vcr.VCR(
before_record_request=before_record_cb,
before_record_request = before_record_cb,
)
with my_vcr.use_cassette('test.yml'):
# your http code here
You can also mutate the request using this callback. For example, you
You can also mutate the response using this callback. For example, you
could remove all query parameters from any requests to the ``'/login'``
path.
@@ -247,7 +246,7 @@ path.
def scrub_login_request(request):
if request.path == '/login':
request.uri, _ = urllib.splitquery(request.uri)
request.uri, _ = urllib.splitquery(response.uri)
return request
my_vcr = vcr.VCR(
@@ -259,12 +258,9 @@ path.
Custom Response Filtering
~~~~~~~~~~~~~~~~~~~~~~~~~
You can also do response filtering with the
``before_record_response`` configuration option. Its usage is
similar to the above ``before_record_request`` - you can
mutate the response, or return ``None`` to avoid recording
the request and response altogether. For example to hide
sensitive data from the request body:
VCR.py also suports response filtering with the
``before_record_response`` keyword argument. It's usage is similar to
that of ``before_record``:
.. code:: python
@@ -306,8 +302,8 @@ in a few ways:
or 0.0.0.0.
- Set the ``ignore_hosts`` configuration option to a list of hosts to
ignore
- Add a ``before_record_request`` or ``before_record_response`` callback
that returns ``None`` for requests you want to ignore (see above).
- Add a ``before_record`` callback that returns None for requests you
want to ignore
Requests that are ignored by VCR will not be saved in a cassette, nor
played back from a cassette. VCR will completely ignore those requests

View File

@@ -1,15 +1,5 @@
Changelog
---------
- 2.0.1 - Fix bug when using vcrpy with python 3.4
- 2.0.0 - Support python 3.7 (fix httplib2 and urllib2, thanks @felixonmars)
[#356] Fixes `before_record_response` so the original response isn't changed (thanks @kgraves)
Fix requests stub when using proxy (thanks @samuelfekete @daneoshiga)
(only for aiohttp stub) Drop support to python 3.4 asyncio.coroutine (aiohttp doesn't support python it anymore)
Fix aiohttp stub to work with aiohttp client (thanks @stj)
Fix aiohttp stub to accept content type passed
Improve docs (thanks @adamchainz)
- 1.13.0 - Fix support to latest aiohttp version (3.3.2). Fix content-type bug in aiohttp stub. Save URL with query params properly when using aiohttp.
- 1.12.0 - Fix support to latest aiohttp version (3.2.1), Adapted setup to PEP508, Support binary responses on aiohttp, Dropped support for EOL python versions (2.6 and 3.3)
- 1.11.1 Fix compatibility with newest requests and urllib3 releases
- 1.11.0 Allow injection of persistence methods + bugfixes (thanks @j-funk and @IvanMalison),
Support python 3.6 + CI tests (thanks @derekbekoe and @graingert),

View File

@@ -12,17 +12,15 @@ Compatibility
VCR.py supports Python 2.7 and 3.4+, and
`pypy <http://pypy.org>`__.
The following HTTP libraries are supported:
The following http libraries are supported:
- ``aiohttp``
- ``boto``
- ``boto3``
- ``http.client``
- ``httplib2``
- ``requests`` (both 1.x and 2.x versions)
- ``tornado.httpclient``
- ``urllib2``
- ``urllib3``
- urllib2
- urllib3
- http.client (python3)
- requests (both 1.x and 2.x versions)
- httplib2
- boto
- Tornado's AsyncHTTPClient
Speed
-----

View File

@@ -28,9 +28,7 @@ install_requires = [
'six>=1.5',
'contextlib2; python_version=="2.7"',
'mock; python_version=="2.7"',
'yarl; python_version>"3.4"',
'yarl<1.0.0; python_version=="3.4"',
'multidict<4.0.0,>=2.0; python_version=="3.4"'
'yarl; python_version>="3.4"',
]
excluded_packages = ["tests*"]
@@ -39,7 +37,7 @@ if sys.version_info[0] == 2:
setup(
name='vcrpy',
version='2.0.1',
version='1.11.1',
description=(
"Automatically mock your HTTP interactions to simplify and "
"speed up testing"
@@ -64,7 +62,6 @@ setup(
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Testing',

View File

@@ -1,33 +1,15 @@
# flake8: noqa
import asyncio
import aiohttp
from aiohttp.test_utils import TestClient
async def aiohttp_request(loop, method, url, output='text', encoding='utf-8', content_type=None, **kwargs):
session = aiohttp.ClientSession(loop=loop)
response_ctx = session.request(method, url, **kwargs)
response = await response_ctx.__aenter__()
if output == 'text':
content = await response.text()
elif output == 'json':
content_type = content_type or 'application/json'
content = await response.json(encoding=encoding, content_type=content_type)
elif output == 'raw':
content = await response.read()
response_ctx._resp.close()
await session.close()
return response, content
def aiohttp_app():
async def hello(request):
return aiohttp.web.Response(text='hello')
app = aiohttp.web.Application()
app.router.add_get('/', hello)
return app
@asyncio.coroutine
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 output == 'text':
content = yield from response.text() # NOQA: E999
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

@@ -0,0 +1,13 @@
import aiohttp
import pytest
import vcr
@vcr.use_cassette()
@pytest.mark.asyncio
async def test_http(): # noqa: E999
async with aiohttp.ClientSession() as session:
url = 'https://httpbin.org/get'
params = {'ham': 'spam'}
resp = await session.get(url, params=params) # noqa: E999
assert (await resp.json())['args'] == {'ham': 'spam'} # noqa: E999

View File

@@ -1,11 +1,18 @@
import contextlib
import pytest
asyncio = pytest.importorskip("asyncio")
aiohttp = pytest.importorskip("aiohttp")
import asyncio # noqa: E402
import contextlib # noqa: E402
import pytest # noqa: E402
import vcr # noqa: E402
from .aiohttp_utils import aiohttp_app, aiohttp_request # noqa: E402
from .aiohttp_utils import aiohttp_request # noqa: E402
try:
from .async_def import test_http # noqa: F401
except SyntaxError:
pass
def run_in_loop(fn):
@@ -71,13 +78,11 @@ def test_text(tmpdir, scheme):
def test_json(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
headers = {'Content-Type': 'application/json'}
with vcr.use_cassette(str(tmpdir.join('json.yaml'))):
_, response_json = get(url, output='json', headers=headers)
_, response_json = get(url, output='json')
with vcr.use_cassette(str(tmpdir.join('json.yaml'))) as cassette:
_, cassette_response_json = get(url, output='json', headers=headers)
_, cassette_response_json = get(url, output='json')
assert cassette_response_json == response_json
assert cassette.play_count == 1
@@ -107,28 +112,24 @@ def test_post(tmpdir, scheme):
def test_params(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
headers = {'Content-Type': 'application/json'}
params = {'a': 1, 'b': False, 'c': 'c'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, output='json', params=params)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, output='json', params=params, headers=headers)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, cassette_response_json = get(url, output='json', params=params, headers=headers)
_, cassette_response_json = get(url, output='json', params=params)
assert cassette_response_json == response_json
assert cassette.play_count == 1
def test_params_same_url_distinct_params(tmpdir, scheme):
url = scheme + '://httpbin.org/get'
headers = {'Content-Type': 'application/json'}
params = {'a': 1, 'b': False, 'c': 'c'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, output='json', params=params)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, output='json', params=params, headers=headers)
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, cassette_response_json = get(url, output='json', params=params, headers=headers)
_, cassette_response_json = get(url, output='json', params=params)
assert cassette_response_json == response_json
assert cassette.play_count == 1
@@ -137,45 +138,3 @@ def test_params_same_url_distinct_params(tmpdir, scheme):
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
def test_params_on_url(tmpdir, scheme):
url = scheme + '://httpbin.org/get?a=1&b=foo'
headers = {'Content-Type': 'application/json'}
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, response_json = get(url, output='json', headers=headers)
request = cassette.requests[0]
assert request.url == url
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
_, cassette_response_json = get(url, output='json', headers=headers)
request = cassette.requests[0]
assert request.url == url
assert cassette_response_json == response_json
assert cassette.play_count == 1
def test_aiohttp_test_client(aiohttp_client, tmpdir):
loop = asyncio.get_event_loop()
app = aiohttp_app()
url = '/'
client = loop.run_until_complete(aiohttp_client(app))
with vcr.use_cassette(str(tmpdir.join('get.yaml'))):
response = loop.run_until_complete(client.get(url))
assert response.status == 200
response_text = loop.run_until_complete(response.text())
assert response_text == 'hello'
response_text = loop.run_until_complete(response.text(errors='replace'))
assert response_text == 'hello'
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
response = loop.run_until_complete(client.get(url))
request = cassette.requests[0]
assert request.url == str(client.make_url(url))
response_text = loop.run_until_complete(response.text())
assert response_text == 'hello'
assert cassette.play_count == 1

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
'''Integration tests with httplib2'''
import sys
# External imports
from six.moves.urllib_parse import urlencode
import pytest
import pytest_httpbin.certs
# Internal imports
import vcr
from assertions import assert_cassette_has_one_response
@@ -19,12 +19,7 @@ def http():
Returns an httplib2 HTTP instance
with the certificate replaced by the httpbin one.
"""
kwargs = {
'ca_certs': pytest_httpbin.certs.where()
}
if sys.version_info[:2] == (3, 7):
kwargs['disable_ssl_certificate_validation'] = True
return httplib2.Http(**kwargs)
return httplib2.Http(ca_certs=pytest_httpbin.certs.where())
def test_response_code(tmpdir, httpbin_both):

View File

@@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
'''Test using a proxy.'''
# External imports
import multiprocessing
import pytest
from six.moves import socketserver, SimpleHTTPServer
from six.moves.urllib.request import urlopen
# Internal imports
import vcr
# Conditional imports
requests = pytest.importorskip("requests")
class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
'''
Simple proxy server.
(Inspired by: http://effbot.org/librarybook/simplehttpserver.htm).
'''
def do_GET(self):
upstream_response = urlopen(self.path)
try:
status = upstream_response.status
headers = upstream_response.headers.items()
except AttributeError:
# In Python 2 the response is an addinfourl instance.
status = upstream_response.code
headers = upstream_response.info().items()
self.send_response(status, upstream_response.msg)
for header in headers:
self.send_header(*header)
self.end_headers()
self.copyfile(upstream_response, self.wfile)
@pytest.yield_fixture(scope='session')
def proxy_server():
httpd = socketserver.ThreadingTCPServer(('', 0), Proxy)
proxy_process = multiprocessing.Process(
target=httpd.serve_forever,
)
proxy_process.start()
yield 'http://{0}:{1}'.format(*httpd.server_address)
proxy_process.terminate()
def test_use_proxy(tmpdir, httpbin, proxy_server):
'''Ensure that it works with a proxy.'''
with vcr.use_cassette(str(tmpdir.join('proxy.yaml'))):
response = requests.get(httpbin.url, proxies={'http': proxy_server})
with vcr.use_cassette(str(tmpdir.join('proxy.yaml'))) as cassette:
cassette_response = requests.get(httpbin.url, proxies={'http': proxy_server})
assert cassette_response.headers == response.headers
assert cassette.play_count == 1

View File

@@ -116,8 +116,8 @@ def test_post_chunked_binary(tmpdir, httpbin):
assert req1 == req2
@pytest.mark.xskip('sys.version_info >= (3, 6)', strict=True, raises=ConnectionError)
@pytest.mark.xskip((3, 5) < sys.version_info < (3, 6) and
@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):

View File

@@ -1,6 +1,5 @@
import vcr
import zlib
import json
import six.moves.http_client as httplib
from assertions import assert_is_json
@@ -84,50 +83,3 @@ def test_original_decoded_response_is_not_modified(tmpdir, httpbin):
assert 'content-encoding' not in inside.headers
assert_is_json(inside.read())
def _make_before_record_response(fields, replacement='[REDACTED]'):
def before_record_response(response):
string_body = response['body']['string'].decode('utf8')
body = json.loads(string_body)
for field in fields:
if field in body:
body[field] = replacement
response['body']['string'] = json.dumps(body).encode()
return response
return before_record_response
def test_original_response_is_not_modified_by_before_filter(tmpdir, httpbin):
testfile = str(tmpdir.join('sensitive_data_scrubbed_response.yml'))
host, port = httpbin.host, httpbin.port
field_to_scrub = 'url'
replacement = '[YOU_CANT_HAVE_THE_MANGO]'
conn = httplib.HTTPConnection(host, port)
conn.request('GET', '/get')
outside = conn.getresponse()
callback = _make_before_record_response([field_to_scrub], replacement)
with vcr.use_cassette(testfile, before_record_response=callback):
conn = httplib.HTTPConnection(host, port)
conn.request('GET', '/get')
inside = conn.getresponse()
# The scrubbed field should be the same, because no cassette existed.
# Furthermore, the responses should be identical.
inside_body = json.loads(inside.read().decode('utf-8'))
outside_body = json.loads(outside.read().decode('utf-8'))
assert not inside_body[field_to_scrub] == replacement
assert inside_body[field_to_scrub] == outside_body[field_to_scrub]
# Ensure that when a cassette exists, the scrubbed response is returned.
with vcr.use_cassette(testfile, before_record_response=callback):
conn = httplib.HTTPConnection(host, port)
conn.request('GET', '/get')
inside = conn.getresponse()
inside_body = json.loads(inside.read().decode('utf-8'))
assert inside_body[field_to_scrub] == replacement

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
'''Integration tests with urllib2'''
import ssl
from six.moves.urllib.request import urlopen
from six.moves.urllib_parse import urlencode
import pytest_httpbin.certs
@@ -13,9 +12,7 @@ from assertions import assert_cassette_has_one_response
def urlopen_with_cafile(*args, **kwargs):
context = ssl.create_default_context(cafile=pytest_httpbin.certs.where())
context.check_hostname = False
kwargs['context'] = context
kwargs['cafile'] = pytest_httpbin.certs.where()
try:
return urlopen(*args, **kwargs)
except TypeError:

View File

@@ -22,9 +22,9 @@ def test_try_migrate_with_yaml(tmpdir):
shutil.copy('tests/fixtures/migration/old_cassette.yaml', cassette)
assert vcr.migration.try_migrate(cassette)
with open('tests/fixtures/migration/new_cassette.yaml', 'r') as f:
expected_yaml = yaml.load(f, Loader=yaml.FullLoader)
expected_yaml = yaml.load(f)
with open(cassette, 'r') as f:
actual_yaml = yaml.load(f, Loader=yaml.FullLoader)
actual_yaml = yaml.load(f)
assert actual_yaml == expected_yaml

View File

@@ -1,5 +1,5 @@
[tox]
envlist = {py27,py34,py35,py36,py37,pypy}-{flakes,requests27,httplib2,urllib3121,tornado4,boto3,aiohttp}
envlist = {py27,py35,py36,pypy}-{flakes,requests27,httplib2,urllib3121,tornado4,boto3,aiohttp}
[testenv:flakes]
skipsdist = True
@@ -11,12 +11,13 @@ deps = flake8
[testenv]
commands =
./runtests.sh {posargs}
./runtests.sh -n 4 {posargs}
deps =
Flask<1
mock
pytest
pytest-httpbin
pytest-xdist
PyYAML
requests27: requests==2.7.0
httplib2: httplib2
@@ -25,9 +26,8 @@ deps =
{py27,py35,py36,pypy}-tornado4: pytest-tornado
{py27,py35,py36}-tornado4: pycurl
boto3: boto3
aiohttp: aiohttp
aiohttp: aiohttp<3
aiohttp: pytest-asyncio
aiohttp: pytest-aiohttp
[flake8]
max_line_length = 110

View File

@@ -1,3 +1,7 @@
async def handle_coroutine(vcr, fn): # noqa: E999
import asyncio
@asyncio.coroutine
def handle_coroutine(vcr, fn):
with vcr as cassette:
return (await fn(cassette)) # noqa: E999
return (yield from fn(cassette)) # noqa: E999

View File

@@ -1,5 +1,4 @@
import collections
import copy
import sys
import inspect
import logging
@@ -16,13 +15,11 @@ from .util import partition_dict
try:
from asyncio import iscoroutinefunction
from ._handle_coroutine import handle_coroutine
except ImportError:
def iscoroutinefunction(*args, **kwargs):
return False
if sys.version_info[:2] >= (3, 5):
from ._handle_coroutine import handle_coroutine
else:
def handle_coroutine(*args, **kwags):
raise NotImplementedError('Not implemented on Python 2')
@@ -138,10 +135,7 @@ class CassetteContextDecorator(object):
except Exception:
to_yield = coroutine.throw(*sys.exc_info())
else:
try:
to_yield = coroutine.send(to_send)
except StopIteration:
break
to_yield = coroutine.send(to_send)
def _handle_function(self, fn):
with self as cassette:
@@ -228,9 +222,6 @@ class Cassette(object):
request = self._before_record_request(request)
if not request:
return
# Deepcopy is here because mutation of `response` will corrupt the
# real response.
response = copy.deepcopy(response)
response = self._before_record_response(response)
if response is None:
return

View File

@@ -1,4 +1,5 @@
import copy
import collections
import functools
import inspect
import os
@@ -13,11 +14,6 @@ from .util import compose, auto_decorate
from . import matchers
from . import filters
try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable
class VCR(object):
@@ -179,7 +175,7 @@ class VCR(object):
if decode_compressed_response:
filter_functions.append(filters.decode_response)
if before_record_response:
if not isinstance(before_record_response, Iterable):
if not isinstance(before_record_response, collections.Iterable):
before_record_response = (before_record_response,)
filter_functions.extend(before_record_response)
@@ -245,7 +241,7 @@ class VCR(object):
filter_functions.append(self._build_ignore_hosts(hosts_to_ignore))
if before_record_request:
if not isinstance(before_record_request, Iterable):
if not isinstance(before_record_request, collections.Iterable):
before_record_request = (before_record_request,)
filter_functions.extend(before_record_request)

View File

@@ -112,7 +112,7 @@ class HeadersDict(CaseInsensitiveDict):
In addition, some servers sometimes send the same header more than once,
and httplib *can* deal with this situation.
Furthermore, I wanted to keep the request and response cassette format as
Futhermore, I wanted to keep the request and response cassette format as
similar as possible.
For this reason, in cassettes I keep a dict with lists as keys, but once

View File

@@ -25,5 +25,5 @@ def serialize(cassette_dict):
original.end,
original.args[-1] + error_message
)
except TypeError: # py3
except TypeError as original: # py3
raise TypeError(error_message)

View File

@@ -18,7 +18,7 @@ log = logging.getLogger(__name__)
class VCRFakeSocket(object):
"""
A socket that doesn't do anything!
Used when playing back cassettes, when there
Used when playing back casssettes, when there
is no actual open socket.
"""
@@ -136,10 +136,7 @@ class VCRConnection(object):
def _uri(self, url):
"""Returns request absolute URI"""
if url and not url.startswith('/'):
# Then this must be a proxy request.
return url
uri = "{0}://{1}{2}{3}".format(
uri = "{}://{}{}{}".format(
self._protocol,
self.real_connection.host,
self._port_postfix(),
@@ -171,8 +168,6 @@ class VCRConnection(object):
# allows me to compare the entire length of the response to see if it
# exists in the cassette.
self._sock = VCRFakeSocket()
def putrequest(self, method, url, *args, **kwargs):
"""
httplib gives you more than one way to do it. This is a way
@@ -296,13 +291,11 @@ class VCRConnection(object):
with force_reset():
return self.real_connection.connect(*args, **kwargs)
self._sock = VCRFakeSocket()
@property
def sock(self):
if self.real_connection.sock:
return self.real_connection.sock
return self._sock
return VCRFakeSocket()
@sock.setter
def sock(self, value):
@@ -320,8 +313,6 @@ class VCRConnection(object):
with force_reset():
self.real_connection = self._baseclass(*args, **kwargs)
self._sock = None
def __setattr__(self, name, value):
"""
We need to define this because any attributes that are set on the

View File

@@ -12,46 +12,37 @@ from vcr.request import Request
class MockClientResponse(ClientResponse):
def __init__(self, method, url):
super().__init__(
method=method,
url=url,
writer=None,
continue100=None,
timer=None,
request_info=None,
traces=None,
loop=asyncio.get_event_loop(),
session=None,
)
# TODO: get encoding from header
@asyncio.coroutine
def json(self, *, encoding='utf-8', loads=json.loads): # NOQA: E999
return loads(self.content.decode(encoding))
async def json(self, *, encoding='utf-8', loads=json.loads, **kwargs): # NOQA: E999
return loads(self._body.decode(encoding))
@asyncio.coroutine
def text(self, encoding='utf-8'):
return self.content.decode(encoding)
async def text(self, encoding='utf-8', errors='strict'):
return self._body.decode(encoding, errors=errors)
async def read(self):
return self._body
@asyncio.coroutine
def read(self):
return self.content
@asyncio.coroutine
def release(self):
pass
def vcr_request(cassette, real_request):
@functools.wraps(real_request)
async def new_request(self, method, url, **kwargs):
@asyncio.coroutine
def new_request(self, method, url, **kwargs):
headers = kwargs.get('headers')
headers = self._prepare_headers(headers)
data = kwargs.get('data')
params = kwargs.get('params')
request_url = URL(url)
if params:
for k, v in params.items():
params[k] = str(v)
request_url = URL(url).with_query(params)
request_url = URL(url).with_query(params)
vcr_request = Request(method, str(request_url), data, headers)
if cassette.can_play_response_for(vcr_request):
@@ -59,9 +50,9 @@ def vcr_request(cassette, real_request):
response = MockClientResponse(method, URL(vcr_response.get('url')))
response.status = vcr_response['status']['code']
response._body = vcr_response['body']['string']
response.content = vcr_response['body']['string']
response.reason = vcr_response['status']['message']
response._headers = vcr_response['headers']
response.headers = vcr_response['headers']
response.close()
return response
@@ -72,11 +63,11 @@ def vcr_request(cassette, real_request):
msg = ("No match for the request {!r} was found. Can't overwrite "
"existing cassette {!r} in your current record mode {!r}.")
msg = msg.format(vcr_request, cassette._path, cassette.record_mode)
response._body = msg.encode()
response.content = msg.encode()
response.close()
return response
response = await real_request(self, method, url, **kwargs) # NOQA: E999
response = yield from real_request(self, method, url, **kwargs) # NOQA: E999
vcr_response = {
'status': {
@@ -84,7 +75,7 @@ def vcr_request(cassette, real_request):
'message': response.reason,
},
'headers': dict(response.headers),
'body': {'string': (await response.read())}, # NOQA: E999
'body': {'string': (yield from response.read())}, # NOQA: E999
'url': response.url,
}
cassette.append(vcr_request, vcr_response)

View File

@@ -40,7 +40,6 @@ class VCRHTTPSConnectionWithTimeout(VCRHTTPSConnection,
'timeout',
'source_address',
'ca_certs',
'disable_ssl_certificate_validation',
}
unknown_keys = set(kwargs.keys()) - safe_keys
safe_kwargs = kwargs.copy()

View File

@@ -1,17 +1,13 @@
import collections
import types
try:
from collections.abc import Mapping, MutableMapping
except ImportError:
from collections import Mapping, MutableMapping
# Shamelessly stolen from https://github.com/kennethreitz/requests/blob/master/requests/structures.py
class CaseInsensitiveDict(MutableMapping):
class CaseInsensitiveDict(collections.MutableMapping):
"""
A case-insensitive ``dict``-like object.
Implements all methods and operations of
``collections.abc.MutableMapping`` as well as dict's ``copy``. Also
``collections.MutableMapping`` as well as dict's ``copy``. Also
provides ``lower_items``.
All keys are expected to be strings. The structure remembers the
case of the last key to be set, and ``iter(instance)``,
@@ -61,7 +57,7 @@ class CaseInsensitiveDict(MutableMapping):
)
def __eq__(self, other):
if isinstance(other, Mapping):
if isinstance(other, collections.Mapping):
other = CaseInsensitiveDict(other)
else:
return NotImplemented