'''Stubs for tornado HTTP clients''' from __future__ import absolute_import from six import BytesIO from tornado import httputil from tornado.httpclient import AsyncHTTPClient from tornado.httpclient import HTTPResponse from tornado.simple_httpclient import SimpleAsyncHTTPClient from vcr.errors import CannotOverwriteExistingCassetteException from vcr.request import Request class _VCRAsyncClient(object): cassette = None def __new__(cls, *args, **kwargs): from vcr.patch import force_reset with force_reset(): return super(_VCRAsyncClient, cls).__new__(cls, *args, **kwargs) def initialize(self, *args, **kwargs): from vcr.patch import force_reset with force_reset(): self.real_client = self._baseclass(*args, **kwargs) @property def io_loop(self): return self.real_client.io_loop @property def _closed(self): return self.real_client._closed @property def defaults(self): return self.real_client.defaults def close(self): from vcr.patch import force_reset with force_reset(): self.real_client.close() def fetch_impl(self, request, callback): headers = dict(request.headers) if request.user_agent: headers.setdefault('User-Agent', request.user_agent) # TODO body_producer, header_callback, and streaming_callback are not # yet supported. unsupported_call = ( request.body_producer is not None or request.header_callback is not None or request.streaming_callback is not None ) if unsupported_call: response = HTTPResponse( request, 599, error=Exception( "The request (%s) uses AsyncHTTPClient functionality " "that is not yet supported by VCR.py. Please make the " "request outside a VCR.py context." % repr(request) ), ) vcr_request = Request( request.method, request.url, request.body, headers, ) if self.cassette.can_play_response_for(vcr_request): vcr_response = self.cassette.play_response(vcr_request) headers = httputil.HTTPHeaders() recorded_headers = vcr_response['headers'] if isinstance(recorded_headers, dict): recorded_headers = recorded_headers.items() for k, vs in recorded_headers: for v in vs: headers.add(k, v) response = HTTPResponse( request, code=vcr_response['status']['code'], reason=vcr_response['status']['message'], headers=headers, buffer=BytesIO(vcr_response['body']['string']), ) callback(response) else: if self.cassette.write_protected and self.cassette.filter_request( vcr_request ): response = HTTPResponse( request, 599, error=CannotOverwriteExistingCassetteException( "No match for the request (%r) was found. " "Can't overwrite existing cassette (%r) in " "your current record mode (%r)." % (vcr_request, self.cassette._path, self.cassette.record_mode) ), ) callback(response) def new_callback(response): headers = [ (k, response.headers.get_list(k)) for k in response.headers.keys() ] vcr_response = { 'status': { 'code': response.code, 'message': response.reason, }, 'headers': headers, 'body': {'string': response.body}, } self.cassette.append(vcr_request, vcr_response) callback(response) from vcr.patch import force_reset with force_reset(): self.real_client.fetch_impl(request, new_callback) class VCRAsyncHTTPClient(_VCRAsyncClient, AsyncHTTPClient): _baseclass = AsyncHTTPClient class VCRSimpleAsyncHTTPClient(_VCRAsyncClient, SimpleAsyncHTTPClient): _baseclass = SimpleAsyncHTTPClient try: from tornado.curl_httpclient import CurlAsyncHTTPClient except ImportError: # pragma: no cover VCRCurlAsyncHTTPClient = None else: class VCRCurlAsyncHTTPClient(_VCRAsyncClient, CurlAsyncHTTPClient): _baseclass = CurlAsyncHTTPClient