diff --git a/cogs/downloader/errors.py b/cogs/downloader/errors.py index 8e053e3c6..630bd2650 100644 --- a/cogs/downloader/errors.py +++ b/cogs/downloader/errors.py @@ -1,3 +1,8 @@ +__all__ = ["DownloaderException", "GitException", "InvalidRepoName", "ExistingGitRepo", + "MissingGitRepo", "CloningError", "CurrentHashError", "HardResetError", + "UpdateError", "GitDiffError", "PipError"] + + class DownloaderException(Exception): """ Base class for Downloader exceptions. @@ -14,7 +19,7 @@ class GitException(DownloaderException): class InvalidRepoName(DownloaderException): """ Throw when a repo name is invalid. Check - the message for a more detailed reason. + the message for a more detailed reason. """ pass @@ -22,7 +27,7 @@ class InvalidRepoName(DownloaderException): class ExistingGitRepo(DownloaderException): """ Thrown when trying to clone into a folder where a - git repo already exists. + git repo already exists. """ pass @@ -30,7 +35,7 @@ class ExistingGitRepo(DownloaderException): class MissingGitRepo(DownloaderException): """ Thrown when a git repo is expected to exist but - does not. + does not. """ pass @@ -45,7 +50,7 @@ class CloningError(GitException): class CurrentHashError(GitException): """ Thrown when git returns a non zero exit code attempting - to determine the current commit hash. + to determine the current commit hash. """ pass @@ -53,7 +58,7 @@ class CurrentHashError(GitException): class HardResetError(GitException): """ Thrown when there is an issue trying to execute a hard reset - (usually prior to a repo update). + (usually prior to a repo update). """ pass diff --git a/cogs/downloader/installable.py b/cogs/downloader/installable.py index 49f80f56f..c7234e3a1 100644 --- a/cogs/downloader/installable.py +++ b/cogs/downloader/installable.py @@ -18,42 +18,19 @@ class InstallableType(Enum): class Installable(RepoJSONMixin): """ Base class for anything the Downloader cog can install. - - Modules - - Repo Libraries - - Other stuff? + - Modules + - Repo Libraries + - Other stuff? """ INFO_FILE_DESCRIPTION = """ - The info.json file may exist inside every package folder in the repo, - it is optional however. This string describes the valid keys within - an info file (and maybe how the Downloader cog uses them). - KEYS (case sensitive): - author (list of strings) - list of names of authors of the cog - bot_version (list of integer) - Min version number of Red in the - format (MAJOR, MINOR, PATCH) - description (string) - A long description of the cog that appears - when a user executes `!cog info` - hidden (bool) - Determines if a cog is available for install. - install_msg (string) - The message that gets displayed when a cog is - installed - required_cogs (map of cogname to repo URL) - A map of required cogs - that this cog depends on. Downloader will not deal with this - functionality but it may be useful for other cogs. - requirements (list of strings) - list of required libraries that are - passed to pip on cog install. SHARED_LIBRARIES do NOT go in this - list. - short (string) - A short description of the cog that appears when - a user executes `!cog list` - tags (list of strings) - A list of strings that are related to the - functionality of the cog. Used to aid in searching. - type (string) - Optional, defaults to COG. Must be either COG or - SHARED_LIBRARY. If SHARED_LIBRARY then HIDDEN will be True. """ def __init__(self, location: Path): """ Base installable initializer. + :param location: Location (file or folder) to the installable. """ super().__init__(location) @@ -90,9 +67,11 @@ class Installable(RepoJSONMixin): async def copy_to(self, target_dir: Path) -> bool: """ Copies this cog/shared_lib to the given directory. This - will overwrite any files in the target directory - :param target_dir: The installation directory to install to. - :return: bool - status of installation + will overwrite any files in the target directory. + + :param pathlib.Path target_dir: The installation directory to install to. + :return: Status of installation + :rtype: bool """ if self._location.is_file(): copy_func = shutil.copy2 @@ -120,7 +99,8 @@ class Installable(RepoJSONMixin): def _process_info_file(self, info_file_path: Path=None) -> MutableMapping[str, Any]: """ Processes an information file. Loads dependencies among other - information into this object. + information into this object. + :type info_file_path: :param info_file_path: Optional path to information file, defaults to `self.__info_file` :return: Raw information dictionary diff --git a/cogs/downloader/repo_manager.py b/cogs/downloader/repo_manager.py index d4729a228..84af608d7 100644 --- a/cogs/downloader/repo_manager.py +++ b/cogs/downloader/repo_manager.py @@ -3,7 +3,7 @@ import json import os from concurrent.futures import ThreadPoolExecutor from pathlib import Path -from typing import Tuple, MutableMapping +from typing import Tuple, MutableMapping, Union from subprocess import run as sp_run, PIPE from sys import executable import pkgutil @@ -166,7 +166,8 @@ class Repo(RepoJSONMixin): async def clone(self) -> Tuple[str]: """ Clones a new repo. - :return: List of available modules from this repo. + + :return: List of available module names from this repo. """ exists, path = self._existing_git_repo() if exists: @@ -203,6 +204,7 @@ class Repo(RepoJSONMixin): async def current_branch(self) -> str: """ Determines the current branch using git commands. + :return: Current branch name """ exists, _ = self._existing_git_repo() @@ -226,6 +228,7 @@ class Repo(RepoJSONMixin): async def current_commit(self, branch: str=None) -> str: """ Determines the current commit hash of the repo. + :param branch: Override for repo's branch attribute :return: Commit hash string """ @@ -253,8 +256,8 @@ class Repo(RepoJSONMixin): async def hard_reset(self, branch: str=None) -> None: """ Performs a hard reset on the current repo. + :param branch: Override for repo branch attribute. - :return: """ if branch is None: branch = self.branch @@ -280,8 +283,9 @@ class Repo(RepoJSONMixin): async def update(self) -> (str, str): """ Updates the current branch of this repo. - :return: Old commit hash - :return: New commit hash + + :return: tuple of (old commit hash, new commit hash) + :rtype: tuple """ curr_branch = await self.current_branch() old_commit = await self.current_commit(branch=curr_branch) @@ -308,9 +312,11 @@ class Repo(RepoJSONMixin): async def install_cog(self, cog: Installable, target_dir: Path) -> bool: """ Copies a cog to the target directory. - :param cog: - :param target_dir: - :return: bool - if installation succeeded + + :param Installable cog: Cog to install. + :param pathlib.Path target_dir: Directory to install the cog in. + :return: Installation success status. + :rtype: bool """ if cog not in self.available_cogs: raise DownloaderException("That cog does not exist in this repo") @@ -326,10 +332,12 @@ class Repo(RepoJSONMixin): async def install_libraries(self, target_dir: Path, libraries: Tuple[Installable]=()) -> bool: """ Copies all shared libraries (or a given subset) to the target - directory. - :param target_dir: - :param libraries: A subset of available libraries - :return: bool - all copies succeeded + directory. + + :param pathlib.Path target_dir: Directory to install shared libraries to. + :param tuple(Installable) libraries: A subset of available libraries. + :return: Status of all installs. + :rtype: bool """ if libraries: if not all([i in self.available_libraries for i in libraries]): @@ -344,11 +352,13 @@ class Repo(RepoJSONMixin): async def install_requirements(self, cog: Installable, target_dir: Path) -> bool: """ Installs the requirements defined by the requirements - attribute on the cog object and puts them in the given - target directory. - :param cog: - :param target_dir: - :return: + attribute on the cog object and puts them in the given + target directory. + + :param Installable cog: Cog for which to install requirements. + :param pathlib.Path target_dir: Path to which to install requirements. + :return: Status of requirements install. + :rtype: bool """ if not target_dir.is_dir(): raise ValueError("Target directory is not a directory.") @@ -359,10 +369,12 @@ class Repo(RepoJSONMixin): async def install_raw_requirements(self, requirements: Tuple[str], target_dir: Path) -> bool: """ Installs a list of requirements using pip and places them into - the given target directory. - :param requirements: - :param target_dir: - :return: + the given target directory. + + :param tuple(str) requirements: List of requirement names to install via pip. + :param pathlib.Path target_dir: Directory to install requirements to. + :return: Status of all requirements install. + :rtype: bool """ if len(requirements) == 0: return True @@ -388,7 +400,8 @@ class Repo(RepoJSONMixin): def available_cogs(self) -> Tuple[Installable]: """ Returns a list of available cogs (not shared libraries and not hidden). - :return: tuple(installable) + + :rtype: tuple(Installable) """ # noinspection PyTypeChecker return tuple( @@ -400,6 +413,8 @@ class Repo(RepoJSONMixin): def available_libraries(self) -> Tuple[Installable]: """ Returns a list of available shared libraries in this repo. + + :rtype: tuple(Installable) """ # noinspection PyTypeChecker return tuple( @@ -447,10 +462,12 @@ class RepoManager: async def add_repo(self, url: str, name: str, branch: str="master") -> Repo: """ Adds a repo and clones it. - :param url: - :param name: - :param branch: - :return: + + :param url: URL of git repo to clone. + :param name: Internal name of repo. + :param branch: Branch to clone. + :return: New repo object representing cloned repo. + :rtype: Repo """ name = self.validate_and_normalize_repo_name(name) if self.does_repo_exist(name): @@ -469,27 +486,31 @@ class RepoManager: return r - def get_repo(self, name: str) -> Repo: + def get_repo(self, name: str) -> Union[Repo, None]: """ Returns a repo object with the given name. + :param name: Repo name - :return: Repo object or None + :return: Repo object or ``None`` if repo does not exist. + :rtype: Union[Repo, None] """ return self._repos.get(name, None) def get_all_repo_names(self) -> Tuple[str]: """ Returns a tuple of all repo names - :return: + + :rtype: tuple(str) """ # noinspection PyTypeChecker return tuple(self._repos.keys()) async def delete_repo(self, name: str): """ - Deletes a repo and its' folders with the given name. - :param name: - :return: + Deletes a repo and its folders with the given name. + + :param name: Name of the repo to delete. + :raises MissingGitRepo: If the repo does not exist. """ repo = self.get_repo(name) if repo is None: @@ -506,10 +527,11 @@ class RepoManager: async def update_all_repos(self) -> MutableMapping[Repo, Tuple[str, str]]: """ - Calls repo.update() on all repos, returns a mapping of repos - that received new commits to a tuple containing old and - new commit hashes. + Calls :py:meth:`Repo.update` on all repos. + :return: + A mapping of :py:class:`Repo` objects that received new commits to a tuple containing old and + new commit hashes. """ ret = {} for _, repo in self._repos.items(): diff --git a/docs/framework_downloader.rst b/docs/framework_downloader.rst index 1c79b8d45..90bfd7dc2 100644 --- a/docs/framework_downloader.rst +++ b/docs/framework_downloader.rst @@ -1,7 +1,45 @@ .. downloader framework reference -Downloader Framework Reference -============================== +Downloader Framework +==================== + +Info.json +********* + +The info.json file may exist inside every package folder in the repo, +it is optional however. This string describes the valid keys within +an info file (and maybe how the Downloader cog uses them). + +KEYS (case sensitive): + +- ``author`` (list of strings) - list of names of authors of the cog + +- ``bot_version`` (list of integer) - Min version number of Red in the format ``(MAJOR, MINOR, PATCH)`` + +- ``description`` (string) - A long description of the cog that appears when a user executes ```!cog info``. + +- ``hidden`` (bool) - Determines if a cog is available for install. + +- ``install_msg`` (string) - The message that gets displayed when a cog is installed + +- ``required_cogs`` (map of cogname to repo URL) - A map of required cogs that this cog depends on. + Downloader will not deal with this functionality but it may be useful for other cogs. + +- ``requirements`` (list of strings) - list of required libraries that are + passed to pip on cog install. ``SHARED_LIBRARIES`` do NOT go in this + list. + +- ``short`` (string) - A short description of the cog that appears when + a user executes `!cog list` + +- ``tags`` (list of strings) - A list of strings that are related to the + functionality of the cog. Used to aid in searching. + +- ``type`` (string) - Optional, defaults to ``COG``. Must be either ``COG`` or + ``SHARED_LIBRARY``. If ``SHARED_LIBRARY`` then ``hidden`` will be ``True``. + +API Reference +************* .. automodule:: cogs.downloader.json_mixins @@ -10,14 +48,29 @@ Downloader Framework Reference .. automodule:: cogs.downloader.installable +Installable +^^^^^^^^^^^ + .. autoclass:: Installable :members: .. automodule:: cogs.downloader.repo_manager +Repo +^^^^ + .. autoclass:: Repo :members: +Repo Manager +^^^^^^^^^^^^ + .. autoclass:: RepoManager :members: +Exceptions +^^^^^^ + +.. automodule:: cogs.downloader.errors + :members: +