[Core] Make Requires.verify() wait until rules are loaded (#2857)

* Make Requires.verify() wait until rules are loaded

Also ensures `Requires` objects are reset when unloaded, particularly in case a `Command` object manages to stay in memory between cog unload and load, and its permissions rules change between those events.

Also, this PR re-ordered some of the event loop policy stuff, because it was required that the event loop policy be set before creating any `Requires` objects. This may or may not have an effect on other `get_event_loop()` calls elsewhere (either in our code, a dependency's, or asyncio's). Either way, these effects would be a *correction*, and any bugs that arise from it are likely to have been occurring silently beforehand.

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>

* Remove calls to `remove_listener()` in permissions

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>

* Fix adding rules for permissions cog/commands itself

Also addresses feedback

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>

* Clean up indentation when setting uvloop policy

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>

* Use `set(walk_commands())` to traverse `Group` subcommands

Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
Toby Harradine
2019-07-14 10:47:40 +10:00
committed by Michael H
parent 21a6384ebf
commit f83f378528
5 changed files with 88 additions and 45 deletions

View File

@@ -272,6 +272,12 @@ class Requires:
`user_perms` will be used exclusively, otherwise, for levels
other than bot owner, the user can still run the command if
they have the required `user_perms`.
ready_event : asyncio.Event
Event for when this Requires object has had its rules loaded.
If permissions is loaded, this should be set when permissions
has finished loading rules into this object. If permissions
is not loaded, it should be set as soon as the command or cog
is added.
user_perms : Optional[discord.Permissions]
The required permissions for users to execute the command. Can
be ``None``, in which case the `privilege_level` will be used
@@ -300,6 +306,7 @@ class Requires:
):
self.checks: List[CheckPredicate] = checks
self.privilege_level: Optional[PrivilegeLevel] = privilege_level
self.ready_event = asyncio.Event()
if isinstance(user_perms, dict):
self.user_perms: Optional[discord.Permissions] = discord.Permissions.none()
@@ -413,6 +420,16 @@ class Requires:
if default is not None:
rules[self.DEFAULT] = default
def reset(self) -> None:
"""Reset this Requires object to its original state.
This will clear all rules, including defaults. It also resets
the `Requires.ready_event`.
"""
self._guild_rules.clear() # pylint: disable=no-member
self._global_rules.clear() # pylint: disable=no-member
self.ready_event.clear()
async def verify(self, ctx: "Context") -> bool:
"""Check if the given context passes the requirements.
@@ -438,6 +455,8 @@ class Requires:
Propogated from any permissions checks.
"""
if not self.ready_event.is_set():
await self.ready_event.wait()
await self._verify_bot(ctx)
# Owner should never be locked out of commands for user permissions.