diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index efa375d52..413d435a2 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -31,6 +31,7 @@ import importlib import sys import traceback import re +import types import discord @@ -98,8 +99,8 @@ class BotBase(GroupMixin): super().__init__(**options) self.command_prefix = command_prefix self.extra_events = {} - self.cogs = {} - self.extensions = {} + self._cogs = {} + self._extensions = {} self._checks = [] self._check_once = [] self._before_invoke = None @@ -128,13 +129,13 @@ class BotBase(GroupMixin): asyncio.ensure_future(coro, loop=self.loop) async def close(self): - for extension in tuple(self.extensions): + for extension in tuple(self._extensions): try: self.unload_extension(extension) except Exception: pass - for cog in tuple(self.cogs): + for cog in tuple(self._cogs): try: self.remove_cog(cog) except Exception: @@ -476,7 +477,7 @@ class BotBase(GroupMixin): raise TypeError('cogs must derive from Cog') cog = cog._inject(self) - self.cogs[cog.__cog_name__] = cog + self._cogs[cog.__cog_name__] = cog def get_cog(self, name): """Gets the cog instance requested. @@ -490,7 +491,7 @@ class BotBase(GroupMixin): This is equivalent to the name passed via keyword argument in class creation or the class name if unspecified. """ - return self.cogs.get(name) + return self._cogs.get(name) def remove_cog(self, name): """Removes a cog from the bot. @@ -506,7 +507,7 @@ class BotBase(GroupMixin): The name of the cog to remove. """ - cog = self.cogs.pop(name, None) + cog = self._cogs.pop(name, None) if cog is None: return @@ -515,6 +516,11 @@ class BotBase(GroupMixin): help_command.cog = None cog._eject(self) + @property + def cogs(self): + """Mapping[:class:`str`, :class:`Cog`]: A read-only mapping of cog name to cog.""" + return types.MappingProxyType(self._cogs) + # extensions def load_extension(self, name): @@ -542,7 +548,7 @@ class BotBase(GroupMixin): The extension could not be imported. """ - if name in self.extensions: + if name in self._extensions: return lib = importlib.import_module(name) @@ -552,7 +558,7 @@ class BotBase(GroupMixin): raise discord.ClientException('extension does not have a setup function') lib.setup(self) - self.extensions[name] = lib + self._extensions[name] = lib def unload_extension(self, name): """Unloads an extension. @@ -573,7 +579,7 @@ class BotBase(GroupMixin): ``foo.test`` if you want to import ``foo/test.py``. """ - lib = self.extensions.get(name) + lib = self._extensions.get(name) if lib is None: return @@ -582,7 +588,7 @@ class BotBase(GroupMixin): # find all references to the module # remove the cogs registered from the module - for cogname, cog in self.cogs.copy().items(): + for cogname, cog in self._cogs.copy().items(): if _is_submodule(lib_name, cog.__module__): self.remove_cog(cogname) @@ -615,12 +621,17 @@ class BotBase(GroupMixin): finally: # finally remove the import.. del lib - del self.extensions[name] + del self._extensions[name] del sys.modules[name] for module in list(sys.modules.keys()): if _is_submodule(lib_name, module): del sys.modules[module] + @property + def extensions(self): + """Mapping[:class:`str`, :class:`py:types.ModuleType`]: A read-only mapping of extension name to extension.""" + return types.MappingProxyType(self._extensions) + # help command stuff @property