From 83211a18873ab81691ab69980630c3994124b46d Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Sat, 20 Sep 2014 10:55:03 -0700 Subject: [PATCH] Make changes from b1cdd50e9b633060a801e9f8669de375fbe946c9 compatible with requests1.x; Update Readme.md with description of `before_record_response` --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++------------ vcr/patch.py | 16 +++++++++---- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0a9c954..5c55b00 100644 --- a/README.md +++ b/README.md @@ -176,13 +176,13 @@ with vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml') as cass: The `Cassette` object exposes the following properties which I consider part of the API. The fields are as follows: - * `requests`: A list of vcr.Request objects containing the requests made while - this cassette was being used, ordered by the order that the request was made. + * `requests`: A list of vcr.Request objects corresponding to the http requests + that were made during the recording of the cassette. The requests appear in the + order that they were originally processed. * `responses`: A list of the responses made. - * `play_count`: The number of times this cassette has had a response played - back - * `all_played`: A boolean indicates whether all the responses have been - played back + * `play_count`: The number of times this cassette has played back a response. + * `all_played`: A boolean indicating whether all the responses have been + played back. * `responses_of(request)`: Access the responses that match a given request The `Request` object has the following properties: @@ -215,7 +215,7 @@ Finally, register your class with VCR to use your new serializer. ```python import vcr -BogoSerializer(object): +class BogoSerializer(object): """ Must implement serialize() and deserialize() methods """ @@ -293,12 +293,12 @@ with my_vcr.use_cassette('test.yml', filter_query_parameters=['api_key']): requests.get('http://api.com/getdata?api_key=secretstring') ``` -### Custom request filtering +### Custom Request filtering -If neither of these covers your use case, you can register a callback that will -manipulate the HTTP request before adding it to the cassette. Use the -`before_record` configuration option to so this. Here is an -example that will never record requests to the /login endpoint. +If neither 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` configuration option to so this. Here is an example that will + never record requests to the /login endpoint. ```python def before_record_cb(request): @@ -312,6 +312,40 @@ with my_vcr.use_cassette('test.yml'): # your http code here ``` +You can also mutate the response using this callback. For example, you could +remove all query parameters from any requests to the `'/login'` path. + +```python +def scrub_login_request(request): + if request.path == '/login': + request.uri, _ = urllib.splitquery(response.uri) + return request + +my_vcr = vcr.VCR( + before_record=scrub_login_request, +) +with my_vcr.use_cassette('test.yml'): + # your http code here +``` + +### 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`: + +```python +def scrub_string(string, replacement=''): + def before_record_reponse(response): + return response['body']['string] = response['body']['string].replace(string, replacement) + return scrub_string + +my_vcr = vcr.VCR( + before_record=scrub_string(settings.USERNAME, 'username'), +) +with my_vcr.use_cassette('test.yml'): + # your http code here +``` + ## Ignore requests If you would like to completely ignore certain requests, you can do it in a @@ -335,7 +369,7 @@ to `brew install libyaml` [[Homebrew](http://mxcl.github.com/homebrew/)]) ## Ruby VCR compatibility -I'm not trying to match the format of the Ruby VCR YAML files. Cassettes +VCR.py does not aim to match the format of the Ruby VCR YAML files. Cassettes generated by Ruby's VCR are not compatible with VCR.py. ## Running VCR's test suite @@ -356,7 +390,7 @@ installed. Also, in order for the boto tests to run, you will need an AWS key. Refer to the [boto documentation](http://boto.readthedocs.org/en/latest/getting_started.html) for -how to set this up. I have marked the boto tests as optional in Travis so you +how to set this up. I have marked the boto tests as optional in Travis so you don't have to worry about them failing if you submit a pull request. @@ -423,6 +457,8 @@ API in version 1.0.x ## Changelog + * 1.1.0 Add `before_record_response`. Fix several bugs related to the context + management of cassettes. * 1.0.3: Fix an issue with requests 2.4 and make sure case sensitivity is consistent across python versions * 1.0.2: Fix an issue with requests 2.3 diff --git a/vcr/patch.py b/vcr/patch.py index a8c5aaa..a3672f4 100644 --- a/vcr/patch.py +++ b/vcr/patch.py @@ -127,22 +127,28 @@ class CassettePatcherBuilder(object): (cpool, 'VerifiedHTTPSConnection', VCRRequestsHTTPSConnection), (cpool, 'VerifiedHTTPSConnection', VCRRequestsHTTPSConnection), (cpool, 'HTTPConnection', VCRRequestsHTTPConnection), - (cpool, 'HTTPConnection', VCRHTTPConnection), + (cpool, 'HTTPSConnection', VCRRequestsHTTPSConnection), (cpool.HTTPConnectionPool, 'ConnectionCls', VCRRequestsHTTPConnection), (cpool.HTTPSConnectionPool, 'ConnectionCls', VCRRequestsHTTPSConnection), # These handle making sure that sessions only use the # connections of the appropriate type. - (cpool.HTTPConnectionPool, '_get_conn', self._patched_get_conn(cpool.HTTPConnectionPool)), - (cpool.HTTPSConnectionPool, '_get_conn', self._patched_get_conn(cpool.HTTPSConnectionPool)), ) + mock_triples += ((cpool.HTTPConnectionPool, '_get_conn', + self._patched_get_conn(cpool.HTTPConnectionPool, + lambda : cpool.HTTPConnection)), + (cpool.HTTPSConnectionPool, '_get_conn', + self._patched_get_conn(cpool.HTTPSConnectionPool, + lambda : cpool.HTTPSConnection))) return self._build_patchers_from_mock_triples(mock_triples) - def _patched_get_conn(self, connection_pool_class): + def _patched_get_conn(self, connection_pool_class, connection_class_getter): get_conn = connection_pool_class._get_conn @functools.wraps(get_conn) def patched_get_conn(pool, timeout=None): connection = get_conn(pool, timeout) - while not isinstance(connection, pool.ConnectionCls): + connection_class = pool.ConnectionCls if hasattr(pool, 'ConnectionCls') \ + else connection_class_getter() + while not isinstance(connection, connection_class): connection = get_conn(pool, timeout) return connection return patched_get_conn