sanic-jwt: Async OPTIONS function not being awaited
Hi. I have the following error:
<CoroWrapper Tester.options() running at test.py:18, created at /fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py:85> was never yielded from
Coroutine object created at (most recent call last, truncated to 10 last lines):
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic/server.py", line 609, in serve
loop.run_forever()
File "/fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py", line 126, in send
return self.gen.send(value)
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic/app.py", line 556, in handle_request
response = await response
File "/fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic_jwt/decorators.py", line 42, in decorated_function
return await utils.call(f, request, *args, **kwargs)
File "/fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic_jwt/utils.py", line 44, in call
fn = fn(*args, **kwargs)
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic/views.py", line 63, in view
return self.dispatch_request(*args, **kwargs)
File "/fidessa/uatutils/Tools/Delivery/main_venv/UGUI_UAT-480w8wdy/lib/python3.6/site-packages/sanic/views.py", line 46, in dispatch_request
return handler(request, *args, **kwargs)
File "/fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py", line 85, in debug_wrapper
return CoroWrapper(gen, None)
/fidessa/uatutils/Tools/Delivery/.pyenv/versions/3.6.4/lib/python3.6/asyncio/coroutines.py:126: RuntimeWarning: coroutine 'Tester.options' was never awaited
return self.gen.send(value)
I ran into this issue in my application and was also able to reproduce it with the following code:
from sanic_cors.core import ALL_METHODS
from sanic.views import HTTPMethodView
from sanic.response import text
from sanic_jwt import Authentication, initialize, protected
from sanic import Sanic, Blueprint
class TestMethodView(HTTPMethodView):
async def options(self, *args, **kwargs):
return text('ok')
class Tester(TestMethodView):
decorators = [protected()]
async def get(self, request):
return text('ok')
bp = Blueprint('bp')
bp.add_route(Tester.as_view(), '/test', methods=ALL_METHODS)
class CustomAuth(Authentication):
async def authenticate(self, request, *args, **kwargs):
return {'username': 'Rich', 'password': 'not secure'}
async def retrieve_user(self, request, payload, *args, **kwargs):
if payload:
user_id = payload.get('username', None)
passwd = payload.get('password', None)
return {'username': user_id, 'password': passwd}
async def extend_payload(self, payload, user=None, *args, **kwargs):
if user:
payload.update({'extra_info': 'awesome!'})
return payload
def make_app():
app = Sanic(__name__)
app.config.SANIC_JWT_AUTHORIZATION_HEADER_PREFIX = 'JWT'
app.config.SANIC_JWT_EXPIRATION_DELTA = 360000
app.config.SANIC_JWT_USER_ID = 'username'
initialize(app, authentication_class=CustomAuth)
app.blueprint(bp)
return app
make_app().go_fast(debug=True, host='0.0.0.0', port=9000)
I should note that ALL_METHODS from sanic_cors is just a list that contains both ‘GET’ and ‘OPTIONS’
Replication steps:
- Log in
- Send an OPTIONS request to /test
I’m using
python==3.6.4
sanic-jwt==1.1.0
sanic==0.7.0
I’ve been able to trace the issue to the call function in sanic-jwt/utils.py. The issue appears to be that somehow the options function isn’t registered as a coroutine like the get function is. Maybe this issue needs to go to Sanic? I’m not sure. But the issue only occurs with sanic-jwt because the Sanic code automatically calls functions and then checks to see if they are awaitable. Relevant Sanic code:
# Run response handler
response = handler(request, *args, **kwargs)
if isawaitable(response):
response = await response
I was able to solve the issue for myself by changing call to:
async def call(fn, *args, **kwargs):
if callable(fn):
fn = fn(*args, **kwargs)
if inspect.iscoroutinefunction(fn) or inspect.isawaitable(fn):
fn = await fn
return fn
I’d be more than happy to raise a PR for this. This is my first gh issue, so please let me know if I do something wrong.
Please let me know if you need any further information.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 16 (2 by maintainers)
Commits related to this issue
- #110 test — committed to ahopkins/sanic-jwt by ahopkins 6 years ago
Thanks so much @vltr! I’ll try out the solution.
Hello @rafmagns-skepa-dreag !
Sorry for the void. We (me and @ahopkins) briefly discussed about this issue (and some others), but we had no spare time to see everything through, we are really sorry. I know there must be a solution for this, for now I think you should consider having a copy of
sanic-jwt
with your workaround (if it is working for you) so it won’t stop your development … Until we find some time to figure everything out. I’m really sorry for this delay.