diff --git a/core/cog_manager.py b/core/cog_manager.py index 8a2daed88..9901a191f 100644 --- a/core/cog_manager.py +++ b/core/cog_manager.py @@ -10,20 +10,15 @@ from core import checks from core.config import Config from core.utils.chat_formatting import box - -class CogManagerException(Exception): - pass - - -class InvalidPath(CogManagerException): - pass - - -class NoModuleFound(CogManagerException): - pass +__all__ = ["CogManager"] class CogManager: + """ + This module allows you to load cogs from multiple directories and even from outside the bot + directory. You may also set a directory for downloader to install new cogs to, the default + being the :code:`cogs/` folder in the root bot directory. + """ def __init__(self, paths: Tuple[str]=(), bot_dir: Path=Path.cwd()): self.conf = Config.get_conf(self, 2938473984732, True) self.conf.register_global( @@ -35,8 +30,7 @@ class CogManager: async def paths(self) -> Tuple[Path, ...]: """ - This will return all currently valid path directories. - :return: + All currently valid path directories. """ conf_paths = await self.conf.paths() other_paths = self._paths @@ -50,8 +44,7 @@ class CogManager: async def install_path(self) -> Path: """ - Returns the install path for 3rd party cogs. - :return: + The install path for 3rd party cogs. """ p = Path(await self.conf.install_path()) return p.resolve() @@ -59,9 +52,17 @@ class CogManager: async def set_install_path(self, path: Path) -> Path: """ Install path setter, will return the absolute path to - the given path. - :param path: - :return: + the given path. + + .. note:: + + The bot will not remember your old cog install path which means + that ALL PREVIOUSLY INSTALLED COGS will now be unfindable. + + :param pathlib.Path path: + The new directory for cog installs. + :raises ValueError: + If :code:`path` is not an existing directory. """ if not path.is_dir(): raise ValueError("The install path must be an existing directory.") @@ -73,8 +74,12 @@ class CogManager: def _ensure_path_obj(path: Union[Path, str]) -> Path: """ Guarantees an object will be a path object. + :param path: - :return: + :type path: + pathlib.Path or str + :rtype: + pathlib.Path """ try: path.exists() @@ -85,13 +90,15 @@ class CogManager: async def add_path(self, path: Union[Path, str]): """ Adds a cog path to current list, will ignore duplicates. Does have - a side effect of removing all invalid paths from the saved path - list. + a side effect of removing all invalid paths from the saved path + list. - Will raise InvalidPath if given anything that does not resolve to - a directory. :param path: - :return: + Path to add. + :type path: + pathlib.Path or str + :raises ValueError: + If :code:`path` does not resolve to an existing directory. """ path = self._ensure_path_obj(path) @@ -100,7 +107,7 @@ class CogManager: path = path.resolve() if not path.is_dir(): - raise InvalidPath("'{}' is not a valid directory.".format(path)) + raise ValueError("'{}' is not a valid directory.".format(path)) if path == await self.install_path(): raise ValueError("Cannot add the install path as an additional path.") @@ -112,8 +119,13 @@ class CogManager: async def remove_path(self, path: Union[Path, str]) -> Tuple[Path, ...]: """ Removes a path from the current paths list. - :param path: + + :param path: Path to remove. + :type path: + pathlib.Path or str :return: + Tuple of new valid paths. + :rtype: tuple """ path = self._ensure_path_obj(path) all_paths = list(await self.paths()) @@ -125,19 +137,25 @@ class CogManager: async def set_paths(self, paths_: List[Path]): """ Sets the current paths list. - :param paths_: - :return: + + :param List[pathlib.Path] paths_: + List of paths to set. """ str_paths = [str(p) for p in paths_] await self.conf.paths.set(str_paths) async def find_cog(self, name: str) -> ModuleSpec: """ - Finds a cog in the list of available path. + Finds a cog in the list of available paths. - Raises NoModuleFound if unavailable. :param name: + Name of the cog to find. + :raises RuntimeError: + If there is no cog with the given name. :return: + A module spec to be used for specialized cog loading. + :rtype: + importlib.machinery.ModuleSpec """ resolved_paths = [str(p.resolve()) for p in await self.paths()] for finder, module_name, _ in pkgutil.iter_modules(resolved_paths): @@ -146,17 +164,16 @@ class CogManager: if spec: return spec - raise NoModuleFound("No module by the name of '{}' was found" - " in any available path.".format(name)) + raise RuntimeError("No module by the name of '{}' was found" + " in any available path.".format(name)) @staticmethod def invalidate_caches(): """ This is an alias for an importlib internal and should be called - any time that a new module has been installed to a cog directory. + any time that a new module has been installed to a cog directory. - *I think.* - :return: + *I think.* """ invalidate_caches() diff --git a/core/core_commands.py b/core/core_commands.py index 47a37c7c3..a38f3de7b 100644 --- a/core/core_commands.py +++ b/core/core_commands.py @@ -11,8 +11,6 @@ import discord import aiohttp import asyncio -from core.cog_manager import NoModuleFound - log = logging.getLogger("red") OWNER_DISCLAIMER = ("⚠ **Only** the person who is hosting Red should be " @@ -30,7 +28,7 @@ class Core: """Loads a package""" try: spec = await ctx.bot.cog_mgr.find_cog(cog_name) - except NoModuleFound: + except RuntimeError: await ctx.send("No module by that name was found in any" " cog path.") return diff --git a/docs/framework_cogmanager.rst b/docs/framework_cogmanager.rst new file mode 100644 index 000000000..0cb3e7e9d --- /dev/null +++ b/docs/framework_cogmanager.rst @@ -0,0 +1,8 @@ +.. cog manager docs + +=========== +Cog Manager +=========== + +.. automodule:: core.cog_manager + :members: diff --git a/docs/index.rst b/docs/index.rst index d7954d971..8b161e278 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,6 +17,7 @@ Welcome to Red - Discord Bot's documentation! :caption: Red Development Framework Reference: framework_bank + framework_cogmanager framework_config framework_downloader