From 443fa9f64fe2770510bbef324900c9bc5eb76334 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Sun, 24 May 2026 20:55:15 +0200 Subject: [PATCH] Fix wrong info being loaded into InstalledModule (#6720) --- redbot/core/_downloader/__init__.py | 9 ++-- redbot/core/_downloader/installable.py | 55 ++++++++++++++++++------- redbot/core/_downloader/json_mixins.py | 6 +-- redbot/core/_downloader/repo_manager.py | 9 ++-- redbot/pytest/downloader.py | 3 +- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/redbot/core/_downloader/__init__.py b/redbot/core/_downloader/__init__.py index 2444b23ad..03cadc9ed 100644 --- a/redbot/core/_downloader/__init__.py +++ b/redbot/core/_downloader/__init__.py @@ -154,9 +154,10 @@ async def installed_cogs() -> Tuple[InstalledModule, ...]: """ installed = await _config.installed_cogs() + install_path = await _cog_mgr.install_path() # noinspection PyTypeChecker return tuple( - InstalledModule.from_json(cog_json, _repo_manager) + InstalledModule.from_json(cog_json, _repo_manager, base_target_dir=install_path) for repo_json in installed.values() for cog_json in repo_json.values() ) @@ -174,7 +175,7 @@ async def installed_libraries() -> Tuple[InstalledModule, ...]: installed = await _config.installed_libraries() # noinspection PyTypeChecker return tuple( - InstalledModule.from_json(lib_json, _repo_manager) + InstalledModule.from_json(lib_json, _repo_manager, base_target_dir=SHAREDLIB_PATH) for repo_json in installed.values() for lib_json in repo_json.values() ) @@ -388,8 +389,8 @@ async def _install_cogs( for commit, cogs_to_install in cogs_by_commit.items(): await repo.checkout(commit) for cog in cogs_to_install: - if await cog.copy_to(await _cog_mgr.install_path()): - installed.append(InstalledModule.from_installable(cog)) + if install_location := await cog.copy_to(await _cog_mgr.install_path()): + installed.append(InstalledModule.from_installable(cog, install_location)) else: failed.append(cog) await repo.checkout(exit_to_commit) diff --git a/redbot/core/_downloader/installable.py b/redbot/core/_downloader/installable.py index 08861dd20..6351495bb 100644 --- a/redbot/core/_downloader/installable.py +++ b/redbot/core/_downloader/installable.py @@ -67,7 +67,14 @@ class Installable(RepoJSONMixin): """ - def __init__(self, location: Path, repo: Optional[Repo] = None, commit: str = ""): + def __init__( + self, + location: Path, + repo: Optional[Repo] = None, + commit: str = "", + *, + info_file: Optional[Path] = None, + ): """Base installable initializer. Parameters @@ -97,7 +104,7 @@ class Installable(RepoJSONMixin): self.tags: Tuple[str, ...] self.type: InstallableType - super().__init__(location) + super().__init__(location, info_file=info_file) def __eq__(self, other: Any) -> bool: # noinspection PyProtectedMember @@ -111,14 +118,14 @@ class Installable(RepoJSONMixin): """`str` : The name of this package.""" return self._location.stem - async def copy_to(self, target_dir: Path) -> bool: + async def copy_to(self, target_dir: Path) -> Optional[Path]: """ Copies this cog/shared_lib to the given directory. This 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 + :return: Install location of the cog or None in case of copy failure. + :rtype: `Path`, optional """ copy_func: Callable[..., Any] if self._location.is_file(): @@ -126,13 +133,14 @@ class Installable(RepoJSONMixin): else: copy_func = functools.partial(shutil.copytree, dirs_exist_ok=True) + dst = target_dir / self._location.name # noinspection PyBroadException try: - copy_func(src=str(self._location), dst=str(target_dir / self._location.name)) + copy_func(src=str(self._location), dst=str(dst)) except: # noqa: E722 log.exception("Error occurred when copying path: %s", self._location) - return False - return True + return None + return dst def _read_info_file(self) -> None: super()._read_info_file() @@ -160,8 +168,13 @@ class InstalledModule(Installable): commit: str = "", pinned: bool = False, json_repo_name: str = "", + *, + info_file: Optional[Path] = None, + install_location: Path, ): - super().__init__(location=location, repo=repo, commit=commit) + info_file = info_file or install_location / self.INFO_FILE_NAME + super().__init__(location=location, repo=repo, commit=commit, info_file=info_file) + self._install_location = install_location self.pinned: bool = pinned if self.type is InstallableType.COG else False # this is here so that Downloader could use real repo name instead of "MISSING_REPO" self._json_repo_name = json_repo_name @@ -178,10 +191,10 @@ class InstalledModule(Installable): @classmethod def from_json( - cls, data: Dict[str, Union[str, bool]], repo_mgr: RepoManager + cls, data: Dict[str, Union[str, bool]], repo_mgr: RepoManager, *, base_target_dir: Path ) -> InstalledModule: repo_name = cast(str, data["repo_name"]) - cog_name = cast(str, data["module_name"]) + module_name = cast(str, data["module_name"]) commit = cast(str, data.get("commit", "")) pinned = cast(bool, data.get("pinned", False)) @@ -192,14 +205,26 @@ class InstalledModule(Installable): else: repo_folder = repo_mgr.repos_folder / "MISSING_REPO" - location = repo_folder / cog_name + location = repo_folder / module_name + install_location = base_target_dir / module_name return cls( - location=location, repo=repo, commit=commit, pinned=pinned, json_repo_name=repo_name + location=location, + repo=repo, + commit=commit, + pinned=pinned, + json_repo_name=repo_name, + install_location=install_location, ) @classmethod - def from_installable(cls, module: Installable, *, pinned: bool = False) -> InstalledModule: + def from_installable( + cls, module: Installable, install_location: Path, *, pinned: bool = False + ) -> InstalledModule: return cls( - location=module._location, repo=module.repo, commit=module.commit, pinned=pinned + location=module._location, + repo=module.repo, + commit=module.commit, + pinned=pinned, + install_location=install_location, ) diff --git a/redbot/core/_downloader/json_mixins.py b/redbot/core/_downloader/json_mixins.py index 441d01647..f09b25833 100644 --- a/redbot/core/_downloader/json_mixins.py +++ b/redbot/core/_downloader/json_mixins.py @@ -1,6 +1,6 @@ import json from pathlib import Path -from typing import Any, Dict, Tuple +from typing import Any, Dict, Optional, Tuple from .info_schemas import REPO_SCHEMA, update_mixin from .log import log @@ -9,7 +9,7 @@ from .log import log class RepoJSONMixin: INFO_FILE_NAME = "info.json" - def __init__(self, repo_folder: Path): + def __init__(self, repo_folder: Path, *, info_file: Optional[Path] = None): self._repo_folder = repo_folder self.author: Tuple[str, ...] @@ -17,7 +17,7 @@ class RepoJSONMixin: self.short: str self.description: str - self._info_file = repo_folder / self.INFO_FILE_NAME + self._info_file = info_file or repo_folder / self.INFO_FILE_NAME self._info: Dict[str, Any] self._read_info_file() diff --git a/redbot/core/_downloader/repo_manager.py b/redbot/core/_downloader/repo_manager.py index 401a6b530..d483aef93 100644 --- a/redbot/core/_downloader/repo_manager.py +++ b/redbot/core/_downloader/repo_manager.py @@ -866,10 +866,11 @@ class Repo(RepoJSONMixin): if not target_dir.exists(): raise ValueError("That target directory does not exist.") - if not await cog.copy_to(target_dir=target_dir): + install_location = await cog.copy_to(target_dir=target_dir) + if not install_location: raise errors.CopyingError("There was an issue during copying of cog's files") - return InstalledModule.from_installable(cog) + return InstalledModule.from_installable(cog, install_location) async def install_libraries( self, target_dir: Path, req_target_dir: Path, libraries: Iterable[Installable] = () @@ -907,11 +908,11 @@ class Repo(RepoJSONMixin): for lib in libraries: if not ( await self.install_requirements(cog=lib, target_dir=req_target_dir) - and await lib.copy_to(target_dir=target_dir) + and (install_location := await lib.copy_to(target_dir=target_dir)) ): failed.append(lib) else: - installed.append(InstalledModule.from_installable(lib)) + installed.append(InstalledModule.from_installable(lib, install_location)) return (tuple(installed), tuple(failed)) return ((), ()) diff --git a/redbot/pytest/downloader.py b/redbot/pytest/downloader.py index 5d8c046b6..9381b37ed 100644 --- a/redbot/pytest/downloader.py +++ b/redbot/pytest/downloader.py @@ -129,8 +129,9 @@ def installed_cog(tmpdir): cog_path = tmpdir.mkdir("test_repo").mkdir("test_installed_cog") info_path = cog_path.join("info.json") info_path.write_text(json.dumps(INFO_JSON), "utf-8") + location = Path(str(cog_path)) - cog_info = InstalledModule(Path(str(cog_path))) + cog_info = InstalledModule(location, install_location=location) return cog_info