mirror of
https://github.com/kevin1024/vcrpy.git
synced 2025-12-09 17:15:35 +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
|
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
|
callback with the ``before_record_request`` configuration option to
|
||||||
cassette. Use the ``before_record_request`` configuration option to so this.
|
manipulate the HTTP request before adding it to the cassette, or return
|
||||||
Here is an example that will never record requests to the /login
|
``None`` to ignore it entirely. Here is an example that will never record
|
||||||
endpoint.
|
requests to the ``'/login'`` path:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
def before_record_cb(request):
|
def before_record_cb(request):
|
||||||
if request.path != '/login':
|
if request.path == '/login':
|
||||||
return request
|
return None
|
||||||
|
return request
|
||||||
|
|
||||||
my_vcr = vcr.VCR(
|
my_vcr = vcr.VCR(
|
||||||
before_record_request = before_record_cb,
|
before_record_request=before_record_cb,
|
||||||
)
|
)
|
||||||
with my_vcr.use_cassette('test.yml'):
|
with my_vcr.use_cassette('test.yml'):
|
||||||
# your http code here
|
# 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'``
|
could remove all query parameters from any requests to the ``'/login'``
|
||||||
path.
|
path.
|
||||||
|
|
||||||
@@ -246,7 +247,7 @@ path.
|
|||||||
|
|
||||||
def scrub_login_request(request):
|
def scrub_login_request(request):
|
||||||
if request.path == '/login':
|
if request.path == '/login':
|
||||||
request.uri, _ = urllib.splitquery(response.uri)
|
request.uri, _ = urllib.splitquery(request.uri)
|
||||||
return request
|
return request
|
||||||
|
|
||||||
my_vcr = vcr.VCR(
|
my_vcr = vcr.VCR(
|
||||||
@@ -258,9 +259,12 @@ path.
|
|||||||
Custom Response Filtering
|
Custom Response Filtering
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
VCR.py also suports response filtering with the
|
You can also do response filtering with the
|
||||||
``before_record_response`` keyword argument. It's usage is similar to
|
``before_record_response`` configuration option. Its usage is
|
||||||
that of ``before_record``:
|
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
|
.. code:: python
|
||||||
|
|
||||||
@@ -302,8 +306,8 @@ in a few ways:
|
|||||||
or 0.0.0.0.
|
or 0.0.0.0.
|
||||||
- Set the ``ignore_hosts`` configuration option to a list of hosts to
|
- Set the ``ignore_hosts`` configuration option to a list of hosts to
|
||||||
ignore
|
ignore
|
||||||
- Add a ``before_record`` callback that returns None for requests you
|
- Add a ``before_record_request`` or ``before_record_response`` callback
|
||||||
want to ignore
|
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
|
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
|
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',
|
'six>=1.5',
|
||||||
'contextlib2; python_version=="2.7"',
|
'contextlib2; python_version=="2.7"',
|
||||||
'mock; 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*"]
|
excluded_packages = ["tests*"]
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ def test_aiohttp_test_client(aiohttp_client, tmpdir):
|
|||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
response_text = loop.run_until_complete(response.text())
|
response_text = loop.run_until_complete(response.text())
|
||||||
assert response_text == 'hello'
|
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:
|
with vcr.use_cassette(str(tmpdir.join('get.yaml'))) as cassette:
|
||||||
response = loop.run_until_complete(client.get(url))
|
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)
|
shutil.copy('tests/fixtures/migration/old_cassette.yaml', cassette)
|
||||||
assert vcr.migration.try_migrate(cassette)
|
assert vcr.migration.try_migrate(cassette)
|
||||||
with open('tests/fixtures/migration/new_cassette.yaml', 'r') as f:
|
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:
|
with open(cassette, 'r') as f:
|
||||||
actual_yaml = yaml.load(f)
|
actual_yaml = yaml.load(f, Loader=yaml.FullLoader)
|
||||||
assert actual_yaml == expected_yaml
|
assert actual_yaml == expected_yaml
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import copy
|
import copy
|
||||||
import collections
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
@@ -14,6 +13,11 @@ from .util import compose, auto_decorate
|
|||||||
from . import matchers
|
from . import matchers
|
||||||
from . import filters
|
from . import filters
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections.abc import Iterable
|
||||||
|
except ImportError:
|
||||||
|
from collections import Iterable
|
||||||
|
|
||||||
|
|
||||||
class VCR(object):
|
class VCR(object):
|
||||||
|
|
||||||
@@ -175,7 +179,7 @@ class VCR(object):
|
|||||||
if decode_compressed_response:
|
if decode_compressed_response:
|
||||||
filter_functions.append(filters.decode_response)
|
filter_functions.append(filters.decode_response)
|
||||||
if before_record_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,)
|
before_record_response = (before_record_response,)
|
||||||
filter_functions.extend(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))
|
filter_functions.append(self._build_ignore_hosts(hosts_to_ignore))
|
||||||
|
|
||||||
if before_record_request:
|
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,)
|
before_record_request = (before_record_request,)
|
||||||
filter_functions.extend(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,
|
In addition, some servers sometimes send the same header more than once,
|
||||||
and httplib *can* deal with this situation.
|
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.
|
similar as possible.
|
||||||
|
|
||||||
For this reason, in cassettes I keep a dict with lists as keys, but once
|
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.end,
|
||||||
original.args[-1] + error_message
|
original.args[-1] + error_message
|
||||||
)
|
)
|
||||||
except TypeError as original: # py3
|
except TypeError: # py3
|
||||||
raise TypeError(error_message)
|
raise TypeError(error_message)
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ class MockClientResponse(ClientResponse):
|
|||||||
async def json(self, *, encoding='utf-8', loads=json.loads, **kwargs): # NOQA: E999
|
async def json(self, *, encoding='utf-8', loads=json.loads, **kwargs): # NOQA: E999
|
||||||
return loads(self._body.decode(encoding))
|
return loads(self._body.decode(encoding))
|
||||||
|
|
||||||
async def text(self, encoding='utf-8'):
|
async def text(self, encoding='utf-8', errors='strict'):
|
||||||
return self._body.decode(encoding)
|
return self._body.decode(encoding, errors=errors)
|
||||||
|
|
||||||
async def read(self):
|
async def read(self):
|
||||||
return self._body
|
return self._body
|
||||||
|
|
||||||
async def release(self):
|
def release(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
vcr/util.py
12
vcr/util.py
@@ -1,13 +1,17 @@
|
|||||||
import collections
|
|
||||||
import types
|
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
|
# 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.
|
A case-insensitive ``dict``-like object.
|
||||||
Implements all methods and operations of
|
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``.
|
provides ``lower_items``.
|
||||||
All keys are expected to be strings. The structure remembers the
|
All keys are expected to be strings. The structure remembers the
|
||||||
case of the last key to be set, and ``iter(instance)``,
|
case of the last key to be set, and ``iter(instance)``,
|
||||||
@@ -57,7 +61,7 @@ class CaseInsensitiveDict(collections.MutableMapping):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, collections.Mapping):
|
if isinstance(other, Mapping):
|
||||||
other = CaseInsensitiveDict(other)
|
other = CaseInsensitiveDict(other)
|
||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|||||||
Reference in New Issue
Block a user