[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

@@ -4,10 +4,9 @@ from .permissions import Permissions
async def setup(bot):
cog = Permissions(bot)
await cog.initialize()
# It's important that these listeners are added prior to load, so
# the permissions commands themselves have rules added.
# Automatic listeners being added in add_cog happen in arbitrary
# order, so we want to circumvent that.
bot.add_listener(cog.red_cog_added, "on_cog_add")
bot.add_listener(cog.red_command_added, "on_command_add")
# We should add the rules for the Permissions cog and its own commands *before* adding the cog.
# The actual listeners ought to skip the ones we're passing here.
await cog._on_cog_add(cog)
for command in cog.__cog_commands__:
await cog._on_command_add(command)
bot.add_cog(cog)

View File

@@ -436,31 +436,41 @@ class Permissions(commands.Cog):
await self._clear_rules(guild_id=ctx.guild.id)
await ctx.tick()
async def red_cog_added(self, cog: commands.Cog) -> None:
@commands.Cog.listener()
async def on_cog_add(self, cog: commands.Cog) -> None:
"""Event listener for `cog_add`.
This loads rules whenever a new cog is added.
Do not convert to using Cog.listener decorator !!
This *must* be added manually prior to cog load, and removed at unload
"""
if cog is self:
# This cog has its rules loaded manually in setup()
return
await self._on_cog_add(cog)
@commands.Cog.listener()
async def on_command_add(self, command: commands.Command) -> None:
"""Event listener for `command_add`.
This loads rules whenever a new command is added.
"""
if command.cog is self:
# This cog's commands have their rules loaded manually in setup()
return
await self._on_command_add(command)
async def _on_cog_add(self, cog: commands.Cog) -> None:
self._load_rules_for(
cog_or_command=cog,
rule_dict=await self.config.custom(COG, cog.__class__.__name__).all(),
)
cog.requires.ready_event.set()
async def red_command_added(self, command: commands.Command) -> None:
"""Event listener for `command_add`.
This loads rules whenever a new command is added.
Do not convert to using Cog.listener decorator !!
This *must* be added manually prior to cog load, and removed at unload
"""
async def _on_command_add(self, command: commands.Command) -> None:
self._load_rules_for(
cog_or_command=command,
rule_dict=await self.config.custom(COMMAND, command.qualified_name).all(),
)
command.requires.ready_event.set()
async def _add_rule(
self, rule: bool, cog_or_cmd: CogOrCommand, model_id: int, guild_id: int
@@ -708,8 +718,6 @@ class Permissions(commands.Cog):
cog_or_command.deny_to(model_id, guild_id=guild_id)
def cog_unload(self) -> None:
self.bot.remove_listener(self.red_cog_added, "on_cog_add")
self.bot.remove_listener(self.red_command_added, "on_command_add")
self.bot.loop.create_task(self._unload_all_rules())
async def _unload_all_rules(self) -> None: