Skip to content

Conversation

DA-344
Copy link
Contributor

@DA-344 DA-344 commented Nov 11, 2024

Summary

This PR refactors the Client.run logic to fix problems involving asyncio due to how the library used the loop:

Needs testing

Exception
  File "/home/container/main.py", line 23, in <module>
    bot.run(BOT_TOKEN)

  File "/home/container/.local/lib/python3.11/site-packages/discord/client.py", line 772, in run
    loop.run_forever()

  File "/usr/local/lib/python3.11/asyncio/base_events.py", line 608, in run_forever
    self._run_once()

  File "/usr/local/lib/python3.11/asyncio/base_events.py", line 1936, in _run_once
    handle._run()

  File "/usr/local/lib/python3.11/asyncio/events.py", line 84, in _run
    self._context.run(self._callback, *self._args)

  File "/usr/local/lib/python3.11/asyncio/selector_events.py", line 956, in _read_ready
    self._read_ready_cb()

  File "/usr/local/lib/python3.11/asyncio/selector_events.py", line 988, in _read_ready__get_buffer
    self._protocol.buffer_updated(nbytes)

  File "/usr/local/lib/python3.11/asyncio/sslproto.py", line 439, in buffer_updated
    self._do_handshake()

  File "/usr/local/lib/python3.11/asyncio/sslproto.py", line 560, in _do_handshake
    self._sslobj.do_handshake()

  File "/usr/local/lib/python3.11/ssl.py", line 979, in do_handshake
    self._sslobj.do_handshake()

Information

  • This PR fixes an issue.
  • This PR adds something new (e.g. new method or parameters).
  • This PR is a breaking change (e.g. methods or parameters removed/renamed).
  • This PR is not a code change (e.g. documentation, README, typehinting,
    examples, ...).

Checklist

  • I have searched the open pull requests for duplicates.
  • If code changes were made then they have been tested.
    • I have updated the documentation to reflect the changes.
  • If type: ignore comments were used, a comment is also left explaining why.
  • I have updated the changelog to include these changes.

@Lulalaby Lulalaby requested review from Dorukyum, NeloBlivion and plun1331 and removed request for ChickenDevs November 11, 2024 23:03
DA-344 and others added 2 commits November 19, 2024 08:23
Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com>
Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com>
Co-authored-by: Dorukyum <53639936+Dorukyum@users.noreply.github.com>
Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com>
@DA-344
Copy link
Contributor Author

DA-344 commented Nov 19, 2024

Applied all the changes Dorukyum requested.

Before merging, I would like some feedback on this discussion message

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the Client.run logic to improve async I/O usage and fix asyncio-related issues that could occur when using the library's event loop handling.

  • Replaces asyncio.iscoroutinefunction with inspect.iscoroutinefunction throughout the codebase for better reliability
  • Refactors Client.run to use modern asyncio.run instead of manual loop management
  • Updates loop handling to be more flexible with None values and better async context management

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
discord/utils.py Added inspect import and replaced asyncio.iscoroutinefunction
discord/state.py Updated loop handling to accept None and added dispatch wrapper
discord/http.py Simplified loop initialization to accept None values
discord/ext/tasks/__init__.py Major refactor of task loop handling and scheduling logic
discord/ext/commands/core.py Replaced asyncio.iscoroutinefunction with inspect equivalent
discord/commands/core.py Replaced asyncio.iscoroutinefunction with inspect equivalent
discord/client.py Major refactor of Client.run and loop management
discord/bot.py Replaced asyncio.iscoroutinefunction with inspect equivalent
CHANGELOG.md Added entry documenting the async I/O fixes

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Signed-off-by: Paillat <paillat@pycord.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: DA344 <108473820+DA-344@users.noreply.github.com>
@Soheab
Copy link
Contributor

Soheab commented Oct 9, 2025

Can't seem to manually call login + connect:

Error

Traceback (most recent call last):
  File "C:\xxx\pr2645.py", line 19, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "C:xxx\uv\python\cpython-3.14.0-windows-x86_64-none\Lib\asyncio\runners.py", line 204, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\xxx\uv\python\cpython-3.14.0-windows-x86_64-none\Lib\asyncio\runners.py", line 127, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\xxx\uv\python\cpython-3.14.0-windows-x86_64-none\Lib\asyncio\base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\xxx\pr2645.py", line 16, in main
    await client.connect()
  File "xxx\.venv\Lib\site-packages\discord\client.py", line 761, in connect
    self.ws = await asyncio.wait_for(coro, timeout=60.0)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\xxx\uv\python\cpython-3.14.0-windows-x86_64-none\Lib\asyncio\tasks.py", line 488, in wait_for
    return await fut
           ^^^^^^^^^
  File "C:\xxx\.venv\Lib\site-packages\discord\gateway.py", line 348, in from_client
    ws = cls(socket, loop=client.loop)
                          ^^^^^^^^^^^
  File "C:xxx\.venv\Lib\site-packages\discord\client.py", line 369, in loop
    raise RuntimeError("loop is not set")
RuntimeError: loop is not set
Unclosed client session

Code

import asyncio
import discord


client = discord.Client()


@client.event
async def on_ready():
    print(f"We have logged in as {client.user}")


async def main():
    await client.login("xxx")
    await client.connect()


asyncio.run(main())

@Soheab
Copy link
Contributor

Soheab commented Oct 9, 2025

ext.tasks seems to be broken and the changes to it aren't in the changelog?

  1. This task runs automatically
@tasks.loop(seconds=1)
# @tasks.loop(seconds=1, create_loop=False) # <- default
async def my_background_task():
    print("Background task is running...")
 

# my_background_task.start() <- does nothing
client.run(...)
  1. This task does not run
@tasks.loop(seconds=1)
# @tasks.loop(seconds=1, create_loop=False) # <- default
async def my_background_task():
    print("Background task is running...")
 

# optional: my_background_task.start()
  1. This task does not run
@tasks.loop(seconds=1, create_loop=True)
async def my_background_task():
    print("Background task is running...")
 

client.run(...)
  1. This task does not ran and logs a warning
@tasks.loop(seconds=1, create_loop=True)
async def my_background_task():
    print("Background task is running...")
 

my_background_task.start()
# console:
# Task was destroyed but it is pending!
# task: <Task pending name='pycord-ext-task (0x1f4fb9d3380): my_background_task' coro=<Loop._loop() running at C:\Users\Sohea\OneDrive\Documents\test\pycord\.venv\Lib\site-packages\discord\ext\tasks\__init__.py:213>>
# <sys>:0: RuntimeWarning: coroutine 'Loop._loop' was never awaited

This is all super confusing and so is the doc of the create_loop parameter.

@Paillat-dev
Copy link
Member

  1. This task does not run

Isn't that the same code as task number one ?

@Soheab
Copy link
Contributor

Soheab commented Oct 9, 2025

Isn't that the same code as task number one ?

Yes but without running the bot, which is what I assumed create_loop allowed.

@Paillat-dev Paillat-dev linked an issue Oct 18, 2025 that may be closed by this pull request
3 tasks
@Paillat-dev Paillat-dev added the priority: medium Medium Priority label Oct 18, 2025
@Paillat-dev Paillat-dev self-assigned this Oct 18, 2025
@Paillat-dev
Copy link
Member

Added medium prio, this fixes multiple issues and is a much needed cleanup, will check everything out to try and find out more about those loop issues.

@Paillat-dev
Copy link
Member

Paillat-dev commented Oct 18, 2025

Can't seem to manually call login + connect:

Also got that same issue.

@DA-344 What do you think about this ? Is there any reason why we couldn't do that:

    @property
    def loop(self) -> asyncio.AbstractEventLoop:
        """The event loop that the client uses for asynchronous operations."""
        if self._loop is None:
            try:
                self._loop = asyncio.get_running_loop()
            except RuntimeError as e:
                raise RuntimeError("loop is not set") from e
        return self._loop

This should also normally allow you to remove the other places where it try: excepts asyncio.get_running_loop since it would be in the property itself

@Paillat-dev
Copy link
Member

Note, Needs testing w/ async autocompletes and typing context manager as well as ext.loop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event loop stalls even with an idle bot

7 participants