Skip to content

refactor: Update Client.run to have a better async I/O usage#2645

Open
DA-344 wants to merge 72 commits intoPycord-Development:masterfrom
DA-344:fix/client-run
Open

refactor: Update Client.run to have a better async I/O usage#2645
DA-344 wants to merge 72 commits intoPycord-Development:masterfrom
DA-344:fix/client-run

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

@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

@Lulalaby
Copy link
Member

clashes with recently merged pr's

@Lulalaby Lulalaby added this to the v2.8 milestone Dec 24, 2025
@Lulalaby Lulalaby removed the on hold label Dec 24, 2025
@Paillat-dev Paillat-dev added priority: high High Priority and removed priority: medium Medium Priority labels Dec 26, 2025
@Paillat-dev
Copy link
Member

@Lumabots When able can you run this for like 24h with some bot ?

([#2905](https://github.com/Pycord-Development/pycord/pull/2905))
- `view=None` in various methods causing an AttributeError.
([#2915](https://github.com/Pycord-Development/pycord/pull/2915))
- Fixed Async I/O errors that could be raised when using `Client.run`.
Copy link
Member

Choose a reason for hiding this comment

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

This probably should be more extensive.

)


def is_ambiguous(dt: datetime.datetime) -> bool:
Copy link
Member

Choose a reason for hiding this comment

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

Not sure what this is doing here. Is it required ? Or is it an additional feature ? In any case it should at least be in the changelog and maybe a separate PR depending on what the others think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is used internally on Loop.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, but it wasn't present before. Does it change / increase the types of accepted inputs for loops ?

Copy link
Contributor Author

@DA-344 DA-344 Feb 12, 2026

Choose a reason for hiding this comment

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

this checks whether a datetime object falls in a ambiguous local time by DST transition (eg Spanish winter & summer local times)

so there is a moment in which the clock is set back and the same local hour occurs twice (that is why it uses fold)

this checks whether the datetime already has timezone data (dt.tzinfo) or uses a utc or fixed-offset datetime.timezone instance.

Copy link
Member

Choose a reason for hiding this comment

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

Please remove the unrelated datetime changes and (if you want) move them to a separate pull request, I feel like this PR is already heavy enough

Copy link
Member

@plun1331 plun1331 left a comment

Choose a reason for hiding this comment

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

This looks like it refactors a large amount of ext.tasks, much of it unrelated to asyncio usage. That should be in a different pull request.

loop: asyncio.AbstractEventLoop | None = None,
name: str | None = MISSING,
overlap: bool | int = False,
create_loop: bool = False,
Copy link
Member

Choose a reason for hiding this comment

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

Technically makes this breaking I believe. True default probably wouldn't be most ideal though?

return task

def dispatch(self, event: str, *args: Any, **kwargs: Any) -> None:
_log.debug("Dispatching event %s", event)
Copy link
Member

Choose a reason for hiding this comment

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

?

**options: Any,
) -> None:
self.loop: asyncio.AbstractEventLoop = loop
self.loop: asyncio.AbstractEventLoop = loop or MISSING
Copy link
Member

Choose a reason for hiding this comment

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

Is there a difference between using None and MISSING here?

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

Labels

hold: testing This pull request requires further testing priority: high High Priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event loop stalls even with an idle bot

9 participants