Background: I am hosting a flask server alongside a discord client
The flask server just needs to pass on messages from the client to discord and from messages from discord to the client.
I am getting the error when I call loop.run_until_complete(sendMsg(request))
I have tried wait_for
in sendMsg
and wait_for
loop.run_until_complete()
I have looked everywhere and haven't found anything so any help would be appreciated.
Code:
import discord
import json
import os
import asyncio
from flask import Flask, request, render_template
from async_timeout import timeout
from threading import Thread
from time import sleep
client = discord.Client()
messages = []
app = Flask(__name__)
def startClient():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
client.run('token')
#
# Discord Events
#
@client.event
async def on_ready():
print('Discord Client Ready')
@client.event
async def on_message(message):
global messages
message.append(message)
#
# Flask Stuff
#
async def sendMsg(request):
await client.send_message(discord.Object('channel id'), request.form['message'])
@app.route("/chat/", methods=['GET', 'POST'])
def chatPage():
global messages
if request.method == 'GET':
return render_template('main.html')
elif request.method == 'POST':
loop = asyncio.new_event_loop()
loop.run_until_complete(sendMsg(request))
return ''
@app.route("/chat/get", methods=['GET'])
def chatGet():
return json.dumps(messages[int(request.args['lastMessageId']):])
# Start everything
os.environ["WERKZEUG_RUN_MAIN"] = 'true'
print('Starting discord.py client')
Thread(target=startClient).start()
print('Starting flask')
app.run(host='0.0.0.0', debug=True)
Traceback:
Traceback (most recent call last):
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/home/SuperKooks/.local/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/mnt/c/Users/SuperKooks/Documents/Coding/HTML/kindle-discord/app.py", line 51, in chatPage
loop.run_until_complete(sendMsg(request))
File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "/mnt/c/Users/SuperKooks/Documents/Coding/HTML/kindle-discord/app.py", line 39, in sendMsg
await client.send_message(discord.Object('382416348007104513'), request.form['message'])
File "/home/SuperKooks/.local/lib/python3.5/site-packages/discord/client.py", line 1152, in send_message
data = yield from self.http.send_message(channel_id, content, guild_id=guild_id, tts=tts, embed=embed)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/discord/http.py", line 137, in request
r = yield from self.session.request(method, url, **kwargs)
File "/home/SuperKooks/.local/lib/python3.5/site-packages/aiohttp/client.py", line 555, in __iter__
resp = yield from self._coro
File "/home/SuperKooks/.local/lib/python3.5/site-packages/aiohttp/client.py", line 197, in _request
with Timeout(timeout, loop=self._loop):
File "/home/SuperKooks/.local/lib/python3.5/site-packages/async_timeout/__init__.py", line 39, in __enter__
return self._do_enter()
File "/home/SuperKooks/.local/lib/python3.5/site-packages/async_timeout/__init__.py", line 76, in _do_enter
raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
aiohttp.ClientSession()
wants to be called inside a coroutine.
Try moving your Client()
initializer into any async def
function
The problem looks like it could be caused by the following:
elif request.method == 'POST':
loop = asyncio.new_event_loop()
loop.run_until_complete(sendMsg(request))
That creates a new event loop and runs sendMsg(request)
in the new loop. However, sendMsg
calls a method on the client
object running in its own event loop. sendMsg
should be submitted to the existing event loop that runs the client in the other thread. To accomplish that, you need to:
startClient
, e.g. to a client_loop
global variable;loop = asyncio.new_event_loop(); loop.run_until_complete(sendMsg(request))
with a call to asyncio.run_coroutine_threadsafe
to submit the coroutine to an event loop already running in a different thread.The submit code would look like this:
elif request.method == 'POST':
# submit the coroutine to the event loop thread
send_fut = asyncio.run_coroutine_threadsafe(sendMsg(request), client_loop)
# wait for the coroutine to finish
send_fut.result()
I fixed this by replacing all calls to asyncio.run
with the asyncio_run
below. It solved both of these errors for me:
RuntimeError: Timeout context manager should be used inside a task
RuntimeError: This event loop is already running
pip install nest-asyncio
import asyncio
import nest_asyncio
def asyncio_run(future, as_task=True):
"""
A better implementation of `asyncio.run`.
:param future: A future or task or call of an async method.
:param as_task: Forces the future to be scheduled as task (needed for e.g. aiohttp).
"""
try:
loop = asyncio.get_running_loop()
except RuntimeError: # no event loop running:
loop = asyncio.new_event_loop()
return loop.run_until_complete(_to_task(future, as_task, loop))
else:
nest_asyncio.apply(loop)
return asyncio.run(_to_task(future, as_task, loop))
def _to_task(future, as_task, loop):
if not as_task or isinstance(future, asyncio.Task):
return future
return loop.create_task(future)
A secondary goal was to be able to think of asyncio.run
as promise.resolve
from the JS world, or Task.Wait
from the .NET world.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With