Skip to content

Conversation

@elenakrittik
Copy link
Contributor

@elenakrittik elenakrittik commented Nov 12, 2023

Summary

Fixes #641

Refactors the codebase by removing loop sharing where possible and using asyncio.get_running_loop() instead. The loop is still shared in places where the code runs on a separate thread like KeepAliveHandler and AudioPlayer. This means that the majority of the library can now assume that there is an event loop already running. Other changes are documented in changelog files.

While the amount of breaking changes may seem horrific, practically most (if not all) use cases are trivial to fix: users will only need to paint a plenty of async/awaits here and there and possibly move any initialization logic from on_ready, bot constructors, etc. into setup_hook. For an example of migrating see test_bot changes (which is now completely gone), or for a real-world example see these three commits on my Avrae fork.

Thanks to the addition of Client.loop property bots that currently do something like bot.loop.create_task will not be affected by the changes (as long as that access happens after the bot is started, of course).

While i have tried my best to test this in as many cases as possible, i would appreciate if someone could port their bot and see if there is anything suspicious happening. Notably, i haven't yet tested voice sending features, even though they probably should be fine as-is. Tested it using examples/basic_voice.py, works like a charm now!

I would also appreciate some hints on how/where to document this change, as i believe while it is easy to fix, it is still something that we need to make as many users as possible aware of even outside of the changelog.

As for the "Port #xxxx" commits: this initially started as ports of the according PRs to discord.py, but since the 4th or so commit work was done without "stealing" code from there. Not sure if there's anything legal needed to be done?

Checklist

  • If code changes were made, then they have been tested (mostly)
    • I have updated the documentation to reflect the changes
    • I have formatted the code properly by running pdm lint
    • I have type-checked the code by running pdm pyright
  • 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, ...)

@shiftinv shiftinv added t: enhancement New feature t: refactor/typing/lint Refactors, typing changes and/or linting changes labels Nov 13, 2023
@shiftinv shiftinv added the 3.0 label Nov 17, 2023
raise NotImplementedError(msg)

if not self._command_sync_flags._sync_enabled or self._is_closed or self.loop.is_closed():
if not self._command_sync_flags._sync_enabled or self._is_closed:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this important? I can't remember why i removed this. I certainly wouldn't expect the (global) loop to be closed while the bot is running

Comment on lines +201 to +202
.. versionchanged:: 3.0
The ``asyncio_debug`` parameter has been removed. Use :meth:`asyncio.loop.set_debug` directly.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be worth to add this back and only deprecate it?

Also, it might be better to upgrade to Sphinx 7.3 first and change the directive to .. versionremoved:: instead.

Comment on lines +372 to +382
if connector:
try:
asyncio.get_running_loop()
except RuntimeError:
msg = (
"`connector` was created outside of an asyncio loop, which will likely cause"
"issues later down the line due to the client and `connector` running on"
"different asyncio loops; consider moving client instantiation to an '`async"
"main`' function and then manually asyncio.run it"
)
raise RuntimeError(msg) from None
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will remove this as this check only works when you create a dedicated loop for the connector but forget to set it as the global running loop, which is a very specific condition. I would rather not have this check at all (and document the requirement that was actually already there the whole time) or do a proper loop equivalency check, but the latter requires accessing private fields (<connector>._loop). Thoughts?

@loop.setter
def loop(self, value: asyncio.AbstractEventLoop) -> None:
warnings.warn(
"Assigning to `Client.loop` is deprecated. Use `asyncio.set_event_loop()` instead.",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This isn't communicated in the docstring of @.loop because setters don't show up in the docs. Should the docstring be adjusted?

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

Labels

3.0 t: enhancement New feature t: refactor/typing/lint Refactors, typing changes and/or linting changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Async Extension and Cog Loading

2 participants