mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-08 16:53:23 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b474c0510 | ||
|
|
114fcd29b4 | ||
|
|
4e990db32e | ||
|
|
c74a857aa4 | ||
|
|
c3705dae9f | ||
|
|
6c166482d9 | ||
|
|
cc9fabf2d9 | ||
|
|
f77442d87b | ||
|
|
602112cd87 | ||
|
|
4ef5205094 | ||
|
|
0d2f49fe8a | ||
|
|
8fdc6dbb68 | ||
|
|
ffc4dca502 | ||
|
|
a9e75a545e |
@@ -221,24 +221,25 @@ Custom Request filtering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If none of these covers your request filtering needs, you can register a
|
||||
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.
|
||||
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:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def before_record_cb(request):
|
||||
if request.path != '/login':
|
||||
return request
|
||||
if request.path == '/login':
|
||||
return None
|
||||
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 response using this callback. For example, you
|
||||
You can also mutate the request using this callback. For example, you
|
||||
could remove all query parameters from any requests to the ``'/login'``
|
||||
path.
|
||||
|
||||
@@ -246,7 +247,7 @@ path.
|
||||
|
||||
def scrub_login_request(request):
|
||||
if request.path == '/login':
|
||||
request.uri, _ = urllib.splitquery(response.uri)
|
||||
request.uri, _ = urllib.splitquery(request.uri)
|
||||
return request
|
||||
|
||||
my_vcr = vcr.VCR(
|
||||
@@ -258,9 +259,12 @@ path.
|
||||
Custom Response Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
VCR.py also suports response filtering with the
|
||||
``before_record_response`` keyword argument. It's usage is similar to
|
||||
that of ``before_record``:
|
||||
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:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@@ -302,8 +306,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`` callback that returns None for requests you
|
||||
want to ignore
|
||||
- Add a ``before_record_request`` or ``before_record_response`` callback
|
||||
that returns ``None`` for requests you want to ignore (see above).
|
||||
|
||||
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
|
||||
|
||||
4
setup.py
4
setup.py
@@ -28,7 +28,9 @@ install_requires = [
|
||||
'six>=1.5',
|
||||
'contextlib2; python_version=="2.7"',
|
||||
'mock; python_version=="2.7"',
|
||||
'yarl; python_version>="3.4"',
|
||||
'yarl; python_version>"3.4"',
|
||||
'yarl<1.0.0; python_version=="3.4"',
|
||||
'multidict<4.0.0,>=2.0; python_version=="3.4"'
|
||||
]
|
||||
|
||||
excluded_packages = ["tests*"]
|
||||
|
||||
@@ -168,6 +168,8 @@ def test_aiohttp_test_client(aiohttp_client, tmpdir):
|
||||
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))
|
||||
|
||||
@@ -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)
|
||||
expected_yaml = yaml.load(f, Loader=yaml.FullLoader)
|
||||
with open(cassette, 'r') as f:
|
||||
actual_yaml = yaml.load(f)
|
||||
actual_yaml = yaml.load(f, Loader=yaml.FullLoader)
|
||||
assert actual_yaml == expected_yaml
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import copy
|
||||
import collections
|
||||
import functools
|
||||
import inspect
|
||||
import os
|
||||
@@ -14,6 +13,11 @@ 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):
|
||||
|
||||
@@ -175,7 +179,7 @@ class VCR(object):
|
||||
if decode_compressed_response:
|
||||
filter_functions.append(filters.decode_response)
|
||||
if before_record_response:
|
||||
if not isinstance(before_record_response, collections.Iterable):
|
||||
if not isinstance(before_record_response, Iterable):
|
||||
before_record_response = (before_record_response,)
|
||||
filter_functions.extend(before_record_response)
|
||||
|
||||
@@ -241,7 +245,7 @@ class VCR(object):
|
||||
filter_functions.append(self._build_ignore_hosts(hosts_to_ignore))
|
||||
|
||||
if before_record_request:
|
||||
if not isinstance(before_record_request, collections.Iterable):
|
||||
if not isinstance(before_record_request, Iterable):
|
||||
before_record_request = (before_record_request,)
|
||||
filter_functions.extend(before_record_request)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Futhermore, I wanted to keep the request and response cassette format as
|
||||
Furthermore, 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
|
||||
|
||||
@@ -25,5 +25,5 @@ def serialize(cassette_dict):
|
||||
original.end,
|
||||
original.args[-1] + error_message
|
||||
)
|
||||
except TypeError as original: # py3
|
||||
except TypeError: # py3
|
||||
raise TypeError(error_message)
|
||||
|
||||
@@ -28,13 +28,13 @@ class MockClientResponse(ClientResponse):
|
||||
async def json(self, *, encoding='utf-8', loads=json.loads, **kwargs): # NOQA: E999
|
||||
return loads(self._body.decode(encoding))
|
||||
|
||||
async def text(self, encoding='utf-8'):
|
||||
return self._body.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
|
||||
|
||||
async def release(self):
|
||||
def release(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
12
vcr/util.py
12
vcr/util.py
@@ -1,13 +1,17 @@
|
||||
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(collections.MutableMapping):
|
||||
class CaseInsensitiveDict(MutableMapping):
|
||||
"""
|
||||
A case-insensitive ``dict``-like object.
|
||||
Implements all methods and operations of
|
||||
``collections.MutableMapping`` as well as dict's ``copy``. Also
|
||||
``collections.abc.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)``,
|
||||
@@ -57,7 +61,7 @@ class CaseInsensitiveDict(collections.MutableMapping):
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, collections.Mapping):
|
||||
if isinstance(other, Mapping):
|
||||
other = CaseInsensitiveDict(other)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
Reference in New Issue
Block a user