I want to attach a middleware to specific handler and if client is not authorized then want to return an error response. However with the following code :
async def middleware_factory(app, handler):
async def auth_handler(request):
if request.headers.get('Authorization') == 'Basic test1234':
return await handler(request)
return web.Response(text='Forbidden', status='403')
return auth_handler
I am getting an exception that :
AssertionError: Handler <function AbstractRoute.__init__.
<locals>.handler_wrapper at 0x10da56bf8> should return response
instance, got <class 'NoneType'> [middlewares [<function
middleware_factory at 0x1104cb268>]]
Documentation states that I should return a response object which I am doing. Still this error. Where am I going wrong?
You can look to an example from official documentation.
But the main concern that if you want to have Middleware Factory - needs to be a function not a coroutine. Also, recommend to use #web.middleware decorator for that.
from aiohttp import web
def middleware_factory(text):
#web.middleware
async def sample_middleware(request, handler):
resp = await handler(request)
resp.text = resp.text + text
return resp
return sample_middleware
Related
I'm trying to implement accept / error logging for an asynchronous gRPC clientwith gRPC AsyncIO API. I would like to handle common errors (like StatusCode.UNAVAILABLE) in one place instead of in every request.
It's easy for the synchronous version with response.exception():
class LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor):
def __init__(self, logger: Logger):
self.logger = logger
def intercept_unary_unary(self, continuation, client_call_details, request):
self.logger.debug(f"{request=}")
response = continuation(client_call_details, request)
if response.exception():
self.logger.exception(f"{response.code()}")
return response
But things get more complicated when using an asynchronous interceptor.
I tried to use try / except, expecting await returns a response, but this did not lead to anything, because await of continuation returns an undone UnaryUnaryCall and it has no method .exception:
# this does not work
class LoggingClientInterceptor(grpc.aio.UnaryUnaryClientInterceptor):
def __init__(self, logger: Logger):
self.logger = logger
async def intercept_unary_unary(self, continuation, client_call_details, request):
self.logger.debug(f"{request=}")
try:
response = await continuation(client_call_details, request)
return response
except Exception as exc:
self.logger.exception(f"{exc}")
I can await the response code and compare it with OK, and then throw an exception, but it seems to me that this is somehow the wrong way: what if I want to add another interceptor?
code = await response.code()
if code != grpc.StatusCode.OK:
raise SmthException
I have searched extensively, including the code in the official repository, but have not found good examples for asynchronous interceptors
I will be glad if someone will show me the reference sample.
Having similar problem and adjust the code a little bit.
class LoggingClientInterceptor(grpc.aio.UnaryUnaryClientInterceptor):
def __init__(self, logger: Logger):
self.logger = logger
async def intercept_unary_unary(self, continuation, client_call_details, request):
self.logger.debug(f"{request=}")
try:
undone_call = await continuation(client_call_details, request)
response = await undone_call
return response
except Exception as exc:
self.logger.exception(f"{exc}")
raise exc
I need to write test case for a function which use to fetch data from API. In there i used httpx.AsyncClient() as context manager. But i don't understand how to write test case for that function.
async def make_dropbox_request(url, payload, dropbox_token):
async with httpx.AsyncClient(timeout=None, follow_redirects=True) as client:
headers = {
'Content-Type': 'application/json',
'authorization': 'Bearer '+ dropbox_token
}
# make the api call
response = await client.post(url, headers=headers, json=payload)
if response.status_code not in [200]:
print('Dropbox Status Code: ' + str(response.status_code))
if response.status_code in [200, 202, 303]:
return json.loads(response.text)
elif response.status_code == 401:
raise DropboxAuthenticationError()
elif response.status_code == 429:
sleep_time = int(response.headers['Retry-After'])
if sleep_time < 1*60:
await asyncio.sleep(sleep_time)
raise DropboxMaxRateLimitError()
raise DropboxMaxDailyRateLimitError()
raise DropboxHTTPError()
I need to write test cases without calling the API. So there for i believe in this case i need to mock client.post() but i do not understand how to do that. If anyone can help me to figure this out that would be really helpful for me.
This image also include my code block
TL;DR: use return_value.__aenter__.return_value to mock the async context.
Assuming you are using Pytest and pytest-mock, your can use the mocker fixture to mock httpx.AsyncClient.
Since the post function is async, you will need to use an AsyncMock.
Finally, since you use an async context, you will also need to use return_value.__aenter__.return_value to properly mock the returned context. Note for a synchronous context, simply use __enter__ instead of __aenter__.
#pytest.fixture
def mock_AsyncClient(mocker: MockerFixture) -> Mock:
mocked_AsyncClient = mocker.patch(f"{TESTED_MODULE}.AsyncClient")
mocked_async_client = Mock()
response = Response(status_code=200)
mocked_async_client.post = AsyncMock(return_value=response)
mocked_AsyncClient.return_value.__aenter__.return_value = mocked_async_client
return mocked_async_client
I also faced with same issue and handled it with patch decorator. I share my code, so that might help for others.
from unittest.mock import patch
import pytest
import httpx
from app.services import your_service
#pytest.mark.anyio
#patch(
'app.services.your_service.httpx.AsyncClient.post',
return_value = httpx.Response(200, json={'id': '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'})
)
async def test_get_id(mocker):
result = await your_service.get_id()
assert result == '9ed7dasdasd-08ff-4ae1-8952-37e3a323eb08'
You can try out the RESPX mocking library to test and mock your HTTPX clients.
In your case, something like this should do it:
async def make_dropbox_request(url, payload, dropbox_token):
...
response = await client.post(url, headers=headers, json=payload)
...
return response.json()
#respx.mock
async def test_dropbox_endpoint():
url = "https://dropbox-api/some-endpoint/"
endpoint = respx.post(url).respond(json={"some": "data"})
result = await make_dropbox_request(url, ..., ...)
assert endpoint.called
assert result == {"some": "data"}
To be dry and not repeat the mocking in each test, you can set up your own pytest fixture, or respx instance, globally that pre-mocks all dropbox api endpoints, and then in each test just alter response/error depending on the scenario for the test, to get full test coverage on make_dropbox_request.
#pytest.fixture()
async def dropbox_mock():
async with respx.mock() as dropbox:
# default endpoints and their responses
dropbox.post("some-endpoint", name="foo").respond(404)
dropbox.post("some-other-endpoint", name="bar").respond(404)
# ^ name routes for access in tests
yield dropbox
async def test_some_case(dropbox_mock):
dropbox_mock["foo"].respond(json={})
....
Usage of async/await was presented in Flask 2.0. (https://flask.palletsprojects.com/en/2.0.x/async-await/)
I am using Flask-RestX so is it possible to use async/await in RestX requests handlers?
Something like:
#api.route('/try-async')
class MyResource(Resource):
#api.expect(some_schema)
async def get(self):
result = await async_function()
return result
is not working and when I try to reach this endpoint I'm getting error:
TypeError: Object of type coroutine is not JSON serializable
Is there any info on that?
Package versions:
flask==2.0.1
flask-restx==0.4.0
and I've also installed flask[async] as documentation suggests.
I've gotten around this by using an internal redirect
#api.route('/try-async')
class MyResource(Resource):
#api.expect(some_schema)
def get(self):
return redirect(url_for('.hidden_async'), code=307)
#api.route('/hidden-async', methods=['GET'])
async def hidden_async():
result = await async_function()
return result
Redirecting with code=307 will ensure any method and body are unchanged after the redirect (Link). So passing data to the async function is possible as well.
#api.route('/try-async')
class MyResource(Resource):
#api.expect(some_schema)
def post(self):
return redirect(url_for('.hidden_async'), code=307)
#api.route('/hidden-async', methods=['POST'])
async def hidden_async():
data = request.get_json()
tasks = [async_function(d) for d in data]
result = await asyncio.gather(tasks)
return result
My target is to validate the access token before each websocket request initialize. To do that I need to make a call to my oauth server. So attempted to add a middleware which will check the access token. I found how to add middleware in this link https://github.com/tornadoweb/tornado/issues/49 which works fine. But the problem is when I am making the call to my oauth server I am doing it asynchronous and it seems like middleware can't be a async. Here is my sample code
app = Application()
async def middleware(request):
userToken = request.headers.get('Authorization')
active = await check_accesstoken(userToken)
if not active:
return error
app(request)
async def check_accesstoken(userToken):
http_client = httpclient.AsyncHTTPClient()
post_data = {'token': userToken, 'scope': 'email phone'}
api_endpoint = 'https://192.168.0.4:1445/oauth2/introspect'
json_data = json.dumps(post_data)
response = await http_client.fetch(api_endpoint,
raise_error=False,
method='POST',
body=json_data
# headers=headers
)
return response.body.active:
def main():
http_server = tornado.httpserver.HTTPServer(middleware)
http_server.listen(PORT, HOST)
tornado.ioloop.IOLoop.current().start()
if __name__ == '__main__':
main()
Getting following error.
RuntimeWarning: coroutine 'middleware' was never awaited
self.request_callback(self.request)
Questions
How to add a aync middleware?
Or should I make a synchronous call to oauth server?
Or is there any other place where I should check the access token?
Yeah, it's more trouble than it's worth to override HTTPServer.
A saner solution that comes to mind is that you can subclass RequestHandler and create a prepare() method on that subclass where you can check the access token.
Then create all your handlers from that subclass. Here's an example:
class BaseHandler(web.RequestHandler):
async def prepare(self):
active = await check_accesstoken(userToken)
if not active:
self.write("Error")
self.finish()
class SomeHandler(BaseHandler):
...
And if you need to create a prepare() method on your handlers as well, just call the BaseHandler's prepare using super().
I'm trying to create a Python script which running Tornado Async http client with fetch and trying to get the response and print the response.body to the screen.
my class code is:
class MyAsyncHTTPClient(httpclient.AsyncHTTPClient):
#gen.coroutine
def _fetch(self, url):
print('send Asynchronous request ...['+url+"] ")
http_response = yield gen.Task(self.fetch, url)
print(http_response.body)
print('got Asynchronous response !!'+str(http_response))
raise gen.Return(http_response.body)
and I'm calling it this way:
async_http_client = MyAsyncHTTPClient()
res_body = async_http_client._fetch(url)
The issue is that I'm not sure how to deal with this code to get the returned value once it's ready.
can you please help?
Thanks!
Editing
I have also tried implementing this function like:
class MyAsyncHTTPClient(httpclient.AsyncHTTPClient):
#gen.coroutine
def _fetch(self, url):
print('send Asynchronous request ...['+url+"] "+str(self))
http_response = yield self.fetch(url)
print('got Asynchronous response !!')
return http_response.body
But I'm having the same results :(
Editing again
I have succeeded running the async class...but without the inherited object self.
I did it like that:
#gen.coroutine
def _fetch_async(self, url):
print('send Asynchronous request ...[' + url + "] ")
http_response = yield httpclient.AsyncHTTPClient().fetch(url)
#http_response = yield self.fetch(url)
print('got Asynchronous response !!')
return http_response.body
and it worked fine.
The issue is that I need to use the inherited object self, and I'm not sure what am I missing here when defining the class.
When debugging I can see that self is very "thin" with its content..
Please let me know what am I doing wrong here.
Thanks!
Asynchronous functions can only be called from other asynchronous functions. You must call _fetch like this:
#gen.coroutine
def f():
async_http_client = MyAsyncHTTPClient()
res_body = yield async_http_client._fetch(url)
If you're not doing this in the context of a tornado server, the best way to call a coroutine from your __main__ block is like this:
tornado.ioloop.IOLoop.current().run_sync(f)