Discord.py: RuntimeError: Timeout context manager should be used inside a task

Created on 10 Aug 2017  路  12Comments  路  Source: Rapptz/discord.py

I'm not sure if this is a discord.py problem or an asyncio problem or just an operator error, so if it isn't discord.py's problem - my apologies.

dl = asyncio.new_event_loop()

dl.run_until_complete(login())

produces

Traceback (most recent call last):
  File "./daemon.py", line 98, in <module>
    dl.run_until_complete(login())
  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 "./daemon.py", line 92, in login
    await client.login(config["token"])
  File "/home/zenforic/.local/lib/python3.5/site-packages/discord/client.py", line 416, in login
    yield from getattr(self, '_login_' + str(n))(*args, **kwargs)
  File "/home/zenforic/.local/lib/python3.5/site-packages/discord/client.py", line 346, in _login_1
    data = yield from self.http.static_login(token, bot=is_bot)
  File "/home/zenforic/.local/lib/python3.5/site-packages/discord/http.py", line 258, in static_login
    data = yield from self.request(Route('GET', '/users/@me'))
  File "/home/zenforic/.local/lib/python3.5/site-packages/discord/http.py", line 137, in request
    r = yield from self.session.request(method, url, **kwargs)
  File "/home/zenforic/.local/lib/python3.5/site-packages/aiohttp/client.py", line 555, in __iter__
    resp = yield from self._coro
  File "/home/zenforic/.local/lib/python3.5/site-packages/aiohttp/client.py", line 197, in _request
    with Timeout(timeout, loop=self._loop):
  File "/home/zenforic/.local/lib/python3.5/site-packages/async_timeout/__init__.py", line 36, in __enter__
    raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x7fd967aa0b00>

however, it does not give the error if I use

loop = asyncio.get_event_loop()

loop.run_until_complete(login())

but the problem with that is it blocks the rest of the file, which won't work.

invalid

Most helpful comment

@Gorialis I was having this same issue and your comment helped me fix it.
For anyone else having this problem, here's what I did:

def start_loop(loop, gen):
    loop.run_until_complete(gen)


class DiscordAccessor:
    discord_loop = asyncio.new_event_loop()
    client = discord.Client(loop=discord_loop)

    def __init__(self):
        self.channel_id = 'channelID'

        self.discord_coroutine = DiscordAccessor.client.start('token')

        self.discord_thread = Thread(target=start_loop, args=(self.discord_loop, self.discord_coroutine))
        self.discord_thread.start()

The event loop is set when initializing the client, and the separate thread blocks on the same loop. Otherwise, the client attempts to run on the event loop of the thread used to create it (client.py:142).

All 12 comments

So I tried running it in the main event loop and async functioned my socket server and tried running the function in the on_ready event and none of my functions or variables could be found. So I don't know what to do. There's nothing in the docs about how to properly login and connect without blocking the event loop. I'm new to Python and I already have grown a dislike for it because of this confusing coroutine crap. Async in C# and Java was confusing, but I got it within 20 minutes. This stuff is just out there. I've been working on this for about 8 hours now, and it's still as much of a mystery as it was when I started.

just use bot.run()

Look at the examples directory, it has plenty of working bots

@khazhyk That is a blocking call. When I use that, it doesn't start my socket server which is also blocking and needs to be started _after_ the bot is connected and the on_ready function has been called.

EDIT:
by bot.run(), I assume you refer to this:

bot = discord.Client()
bot.run("token")

correct?

Try to avoid using blocking libraries when using discord.py

If you want to have a socket server running in the same process as the bot, you might want to look into asyncio.start_server instead.

If your socket server is blocking, just launch it in a thread? that's what they're for?

If your socket server uses asyncio, you can start it in on_ready using a non-blocking run function (which should exist)

Honestly, I don't know. It's probably not, since I copied and modified the Python doc's TCP echo example and modified it to be able to handle multiple connections simultaneously using os.fork() and replaced the echo part of it, with a reply of "0", "1", or no reply depending on the errors and such. Like I said, I'm new to Python.

OK, I've completely removed the socket server. I'm going to try the asyncio.start_server and hope it works... That is, unless you have a better idea on how I can activate the my noadmin function from another Python hosted next to it?

If you're using a custom loop without setting it as the thread's event loop you need to pass it when creating Client if you're expecting it to work correctly.

@Gorialis I'm not sure I understand what you mean

whelp... I converted to asyncio.start_server however, I cannot test it because I'm back to the problem of having "inaccessible" functions and variables. It's like on_ready is in a different class...

well... I seem to have fixed the inaccessible variables, but now the handler I defined isn't being run... I think I'm done for the night. Evil language.

@Gorialis I was having this same issue and your comment helped me fix it.
For anyone else having this problem, here's what I did:

def start_loop(loop, gen):
    loop.run_until_complete(gen)


class DiscordAccessor:
    discord_loop = asyncio.new_event_loop()
    client = discord.Client(loop=discord_loop)

    def __init__(self):
        self.channel_id = 'channelID'

        self.discord_coroutine = DiscordAccessor.client.start('token')

        self.discord_thread = Thread(target=start_loop, args=(self.discord_loop, self.discord_coroutine))
        self.discord_thread.start()

The event loop is set when initializing the client, and the separate thread blocks on the same loop. Otherwise, the client attempts to run on the event loop of the thread used to create it (client.py:142).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Be4Vision picture Be4Vision  路  3Comments

ghost picture ghost  路  3Comments

reuscam picture reuscam  路  3Comments

TunaHobby picture TunaHobby  路  3Comments

danshat picture danshat  路  3Comments