I'm really confused by the tornado frame work and the 'future' object.
So I want to get a async response by making a http call
Code is:
class TestAsyncHttp(object):
def __init__(self):
self._http_client = httpclient.AsyncHTTPClient()
#gen.coroutine
def get_response(self, params)
response = yield self._request(
method='POST',
endpoint='test'
data=params
)
raise gen.Return(response)
#gen.coroutine
def _request(self, method, endpoint, data):
url = self._make_url(endpoint) #this includes the port..
headers = self._load_headers()
request = httpclient.HTTPRequest(
url,
method=method,
headers=header,
body=json.dump(data)
)
response = yield self._http_client.fetch(request)
raise gen.Return(response)
The thing is, after I finished this one, how can I test it?
I tried to write a scrip which contains...:
import json
with open('test/request.json') as json_file:
request_json = json.loads(json_file.read())
def get_response():
x = TestAsyncHttp()
ret = yield x.get_response(request_json)
body = ret.body
print body['value']
get_response
But then I 'python "path-to-the-script"'
There's nothing output.
If I just stepped into the "python" environment, I got "future" object doesn't have getitem
..How can I get the content from a future..?
Thanks!
Use run_sync to run an async coroutine in a synchronous fashion:
def get_response():
x = TestAsyncHttp()
ret = IOLoop.current().run_sync(lambda: x.get_response(request_json))
body = ret.body
print body['value']
The lambda is required here simply to pass the request_json parameter. If get_response took no arguments, you could instead do:
ret = IOLoop.current().run_sync(x.get_response)
Related
I'm trying to test a method (Creator.do_a_call) which calls another class's method (Requester.make_request) which makes external calls (all the code is below). So I want to mock Request.make_request to return a mocked out response object so it doesn't do any external calls, but when I do that it gives me an error (see below)
Here's the code:
Requester.py:
from requests import Response, Session, Request
class Requester:
url: str = ''
def __init__(self, url: str):
self.url = url
def make_request(self, path: str, method: str = 'get', body: dict = None, custom_headers: dict = None) -> Response:
print("make_request")
url = self.url + path
session = Session()
request = Request(method, url, json=body)
response = session.send(request.prepare())
return response
Creator.py
from Requester import Requester
from http import HTTPStatus as status
from requests import Response
class Creator:
def do_a_call(self, url) -> Response:
print("do_a_call")
requester = Requester(url)
response = requester.make_request(
"/",
"GET"
)
print(type(response))
if response.status_code != status.OK and response.status_code != status.ACCEPTED:
raise Exception(response.status_code, response.text)
return response
main.py
from Creator import Creator
print("main")
creator = Creator()
response = creator.do_a_call("https://google.com")
print(response)
test.py
import unittest
from unittest.mock import patch
from Creator import Creator
from requests import Session
import requests_mock
class CreatorTest(unittest.TestCase):
def mocked_make_request(self, two, three):
session = Session()
mocker = requests_mock.Mocker(session=session)
adapter = requests_mock.Adapter()
adapter.register_uri('POST', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
return mocker.post("mock://test.com", status_code=400)
#patch('Requester.Requester.make_request')
def test_do_a_call_side_effect(self, make_request):
creator = Creator()
make_request.side_effect = self.mocked_make_request
response = creator.do_a_call("mock://test.com")
self.assertEqual(200, response.status_code)
#patch('Requester.Requester.make_request')
def test_do_a_call_return_value(self, make_request):
creator = Creator()
make_request.return_value = self.mocked_make_request("", "")
response = creator.do_a_call("mock://test.com")
self.assertEqual(200, response.status_code)
if __name__ == "__main__":
unittest.main()
In my research I've found two ways that should work - changing the return_value of the mock, or changing the side_effect of the mock, both of which give me the same error:
Traceback (most recent call last):
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/unittest/mock.py", line 1325, in patched
return func(*newargs, **newkeywargs)
File "test.py", line 19, in test_do_a_call_side_effect
response = creator.do_a_call("mock://test.com")
File "/Users/citrja/Documents/git/python_test/Creator.py", line 14, in do_a_call
if response.status_code != status.OK and response.status_code != status.ACCEPTED:
AttributeError: '_Matcher' object has no attribute 'status_code'
I've found out what was wrong! I Was not attaching the adapter to the session, I had thought this happened automagically but apparently not. Here's the updated mocked_make_request function:
def mocked_make_request(self, two, three):
session = Session()
mocker = requests_mock.Mocker(session=session)
adapter = requests_mock.Adapter()
adapter.register_uri('GET', 'mock://test.com', text="Invalid policy number supplied", status_code=400)
session.mount("mock://", adapter)
return session.get("mock://test.com")
I want to invoke a lambda function synchronously (request - response) but want to use python async-await to await the response.
response = await client.invoke('my-func', InvocationType='RequestResponse', Payload='...'))
I found a kind of solution here but it is cumbersome and from 2016.
Is there a better approach today?
I found a way of doing it by manually running the invoke function on the asyncio event loop:
import asyncio
import concurrent
import boto3
import json
import botocore
class LambdaClient():
def __init__(self, concurrency: int = 20):
self.executor = concurrent.futures.ThreadPoolExecutor(
max_workers=concurrency,
)
client_config = botocore.config.Config(
max_pool_connections=concurrency
)
self.client = boto3.client('lambda', config=client_config)
async def invoke_async(self, snapshot):
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(self.executor, lambda: self.invoke(snapshot))
return result
def invoke(self, snapshot):
payload = {
'path': '/calculate/value',
'body': json.dumps(snapshot)
}
b = bytes(json.dumps(payload), encoding='utf8')
response = self.client.invoke(
FunctionName='function-name',
InvocationType='RequestResponse',
LogType='None',
Payload=b)
if 'StatusCode' not in response or response['StatusCode'] != 200:
raise ValueError(f'Lambda invocation failed with response {response}')
output = response["Payload"].read()
return output
I am using rest client in my mozilla browser to call an auth service.
When i pass my credentials in Body, i get an "auth-token" . I then set this token in the header in the browser HEADERS tab.
I have to parse this header which i am setting in the browser in my python script as a variable. Further, after getting this value in my script i have to authenticate the token for its validity.
However i am unable to get the tokens value in my script. My auth function is ready. I just have to fetch the token
How should i fetch this token value from the header ??
Code:
def check_authentication(auth):
print "Auth" , auth
chek_auth_url = ("http://10.168.2.161/auth/v/%s" % (auth))
auth = requests.get(chek_auth_url)
if auth.status_code == 200:
return True
I have to pass the token as a paramter in this function and call in this function in main for authentication.
def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
#h['Access-Control-Allow-Headers'] = "Content-Type"
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
#app.route('/test', methods=['POST', 'OPTIONS'])
#crossdomain(origin='*', headers='Content-Type')
def get_storage():
*check_authentication is called here and token is passed as a parameter*
*if token is valid further task i hav to do*
if __name__ == '__main__':
app.run(host='192.168.56.1', port=8080, threaded=True)
Self-Help is the best help..
Finally i found a fix:
The token value is fetched in the variable tokenValue. I can now do my further coding.
tokenValue = request.headers.get("token")
if tokenValue == None:
return "x-auth-token not passed in header, please pass the token."
else:
print "Token passed is", tokenValue
I have a working application that accepts SOAP requests, processes the requests, forwards the SOAP request to an API, processes the response, and then forwards the response to the client.
I'm trying to change this application so that it will be JSON between my application and the client but still use SOAP between API and my application
Now, it can successfully accept JSON requests from client and send/receive SOAP with API. However, all the responses to client are empty.
The only case that I receive a non-empty response is when there are validation errors with my JSON request.
Here are some code that might be relevant
app = Application([MyServer],
MY_NAMESPACE,
in_protocol=JsonDocument(validator='soft'),
out_protocol=JsonDocument())
application_server = csrf_exempt(MyDjangoApplication(app))
definition of MyDjangoApplication
class MyDjangoApplication(DjangoApplication):
def __call__(self, request, *args, **kwargs):
retval = self.HttpResponseObject()
def start_response(status, headers):
# Status is one of spyne.const.http
status, reason = status.split(' ', 1)
retval.status_code = int(status)
for header, value in headers:
retval[header] = value
environ = request.META.copy()
if request.method == 'POST':
response = self.handle_rpc(environ, start_response)
else:
home_path = reverse('proxy:list_method')
uri = MY_ENDPOINT_URL or request.build_absolute_uri(home_path)
# to generate wsdl content
response = self._WsgiApplication__handle_wsdl_request(environ, start_response, uri)
if request.path == home_path and _is_wsdl_request(environ):
fn = None
elif 'method_name' in kwargs:
fn = view_method
else:
fn = list_method
if fn:
return fn(request, app=self, *args, **kwargs)
self.set_response(retval, response)
return retval
Definition of MyServer
class MyServer(ServiceBase):
#rpc(MyTestMethodRequest, Sign, **method(_returns=MyTestMethodResponse))
#check_method()
def TestMethod(ctx, request, signature):
response = {
'Data': "test"
}
return response
Definitions of MyTestMethodRequest, MyTestMethodResponse:
class MyTestMethodRequest(ComplexModel):
__namespace__ = MY_NAMESPACE
MyString = String(encoding=STR_ENCODING)
class MyTestMethodResponse(ComplexModel):
__namespace__ = MY_NAMESPACE
Data = String(encoding=STR_ENCODING)
Definition of check_method:
def check_method(error_handler=None):
def _check_method(func):
method_name = func.__name__
def __check_method(ctx, request, signature, *args, **kwargs):
if hasattr(request, '__dict__'):
request = request.__dict__
if hasattr(signature, '__dict__'):
signature = signature.__dict__
response = func(ctx, request or {}, signature or {}, *args, **kwargs)
# setting output protocol
output_message = generate_out_string(ctx, [response])
return response
__check_method.__name__ = method_name
__check_method.__doc__ = func.__doc__
return __check_method
return _check_method
Definition of generate_out_string:
def generate_out_string(ctx, objects):
ctx.out_protocol = ctx.in_protocol
return _generate_out_string(ctx, objects)
def _generate_out_string(ctx, objects):
protocol = ctx.out_protocol
ctx.out_object = objects
protocol.serialize(ctx, protocol.RESPONSE)
protocol.create_out_string(ctx)
out_string = list(ctx.out_string)
return out_string[0] if out_string else ''
Note: Most of these definitions have been simplified (I have removed lines which I think are not relevant)
Looking at the code you posted, I can't say I understand what good all those additional decorators and modifiers around arguments do.
Removing them should fix all of your problems.
So let:
class MyTestMethodRequest(ComplexModel):
__namespace__ = MY_NAMESPACE
MyString = Unicode
class MyTestMethodResponse(ComplexModel):
__namespace__ = MY_NAMESPACE
Data = Unicode
Assuming you have the following service:
class MyService(ServiceBase):
#rpc(MyTestMethodRequest, Sign, _returns=MyTestMethodResponse)
def TestMethod(ctx, request, signature):
return MyTestMethodResponse(data="test")
You can have:
app_json = Application([MyService],
MY_NAMESPACE,
in_protocol=JsonDocument(validator='soft'),
out_protocol=JsonDocument())
and
app_soap = Application([MyService],
MY_NAMESPACE,
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11())
which in turn you can pass to DjangoApplication as usual.:
app_json_dja = csrf_exempt(DjangoApplication(app_json))
app_soap_dja = csrf_exempt(DjangoApplication(app_soap))
which in turn you can mount in Django's url router.
I hope this helps!
Suppose my django/flask application pulls in information from API's, how can I test that connection exceptions are caught and handled properly?
So for example here is a function that calls an API:
import requests
def call_the_api():
url = 'http://httpbin.org/get'
try:
req = requests.get(url)
if req.json().get('errors'):
logger.warn("API error response")
return {'request_error': 'api_error_response'}
except requests.exceptions.ConnectionError:
logger.warn('ConnectionError')
return {'request_error': 'ConnectionTimeout'}
except requests.exception.Timeout:
logger.warn('API request timed out')
return {'request_error': 'Timeout'}
except Exception, ex:
logger.warn("API request Exception: %s", ex)
return {'request_error': ex}
else:
return req.json()
For testing responses from the API I found mock to be very useful.
def mock_get_request():
response = requests.get.return_value
json_file = 'sample_response.json'
json_file_path = os.path.join(os.path.dirname(__file__), json_file)
with open(json_file_path, 'r') as f:
response.content = response.text = f.read()
response.status_code = 200
response.encoding = 'utf-8'
response.json = lambda: json.loads(response.content.decode(response.encoding))
response.url = u'%s' % args[0]
return response
class TestSuitabilityFunctions(TestCase):
def test_call_the_api(self):
requests.get = MagicMock(side_effect=mock_get_request)
resp = call_the_api()
self.assertEqual(resp.get('url'), "http://httpbin.org/get")
So my question is how would I go about simulating a connection timeout or error?
Untested code but...
def connection_error():
raise requests.exceptions.ConnectionError
class TestSuitabilityFunctions(TestCase):
#patch.object(module_that_youre_testing, "requests")
def test_connection_error(self, mock_requests):
mock_requests.get = MagicMock(side_effect=connection_error)
with self.assertRaises(requests.exceptions.ConnectionError) as cm:
resp = call_the_api()
exception = cm.exception
self.assertEqual(resp, {'request_error': 'ConnectionTimeout'})
... or similar should do the trick. Off the top of my head I can't remember how assertRaises interacts with errors that are caught. Maybe you don't even need the assertRaises part.