From 643dced57e94d70cc5cfe31f6ba6c271ab8d1cb4 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 8 Feb 2023 19:10:52 +0800 Subject: [PATCH] fix: Fix the log leakage in installation (#1689) --- news/1635.bugfix.md | 1 + src/pdm/cli/actions.py | 9 ++++--- src/pdm/installers/synchronizers.py | 38 ++++++++++++++++++----------- 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 news/1635.bugfix.md diff --git a/news/1635.bugfix.md b/news/1635.bugfix.md new file mode 100644 index 0000000000..1968a22d35 --- /dev/null +++ b/news/1635.bugfix.md @@ -0,0 +1 @@ +Fix a bug that install warning prints to terminal under non-verbose mode. diff --git a/src/pdm/cli/actions.py b/src/pdm/cli/actions.py index 25d65dfd16..e28f800a47 100644 --- a/src/pdm/cli/actions.py +++ b/src/pdm/cli/actions.py @@ -194,7 +194,7 @@ def do_sync( candidates = resolve_candidates_from_lockfile(project, requirements) if tracked_names and dry_run: candidates = {name: c for name, c in candidates.items() if name in tracked_names} - handler = project.core.synchronizer_class( + synchronizer = project.core.synchronizer_class( candidates, project.environment, clean, @@ -205,9 +205,10 @@ def do_sync( reinstall=reinstall, only_keep=only_keep, ) - hooks.try_emit("pre_install", candidates=candidates, dry_run=dry_run) - handler.synchronize() - hooks.try_emit("post_install", candidates=candidates, dry_run=dry_run) + with project.core.ui.logging("install"): + hooks.try_emit("pre_install", candidates=candidates, dry_run=dry_run) + synchronizer.synchronize() + hooks.try_emit("post_install", candidates=candidates, dry_run=dry_run) def do_add( diff --git a/src/pdm/installers/synchronizers.py b/src/pdm/installers/synchronizers.py index 133be2cffb..77cca937f9 100644 --- a/src/pdm/installers/synchronizers.py +++ b/src/pdm/installers/synchronizers.py @@ -10,6 +10,7 @@ from rich.progress import SpinnerColumn from pdm import termui +from pdm.compat import cached_property from pdm.exceptions import InstallationError from pdm.installers.manager import InstallManager from pdm.models.candidates import Candidate, make_candidate @@ -106,6 +107,7 @@ def __init__( reinstall: bool = False, only_keep: bool = False, ) -> None: + self.requested_candidates = candidates self.environment = environment self.clean = clean self.dry_run = dry_run @@ -115,39 +117,45 @@ def __init__( self.use_install_cache = use_install_cache self.reinstall = reinstall self.only_keep = only_keep - self.parallel = environment.project.config["install.parallel"] - locked_repository = environment.project.locked_repository - self.all_candidate_keys = list(locked_repository.all_candidates) + self.working_set = environment.get_working_set() self.ui = environment.project.core.ui - self.self_candidate: Candidate | None = None - if self.install_self: - self.self_candidate = self.environment.project.make_self_candidate(not self.no_editable) + self._manager: InstallManager | None = None + @cached_property + def self_candidate(self) -> Candidate: + """Return the candidate for self project""" + return self.environment.project.make_self_candidate(not self.no_editable) + + @cached_property + def candidates(self) -> dict[str, Candidate]: + """Return the candidates to be installed""" + candidates = self.requested_candidates.copy() if isinstance(self.no_editable, Collection): keys = self.no_editable elif self.no_editable: keys = candidates.keys() else: keys = [] - if self.should_install_editables() and "editables" not in candidates: + if self.should_install_editables(): # Install `editables` as well as required by self project - editables = editables_candidate(environment) + editables = editables_candidate(self.environment) if editables is not None: candidates["editables"] = editables for key in keys: if key in candidates and candidates[key].req.editable: - # We do not do in-place update, which will break the caches candidate = candidates[key] + # Create a new candidate with editable=False req = dataclasses.replace(candidate.req, editable=False) candidates[key] = make_candidate(req, candidate.name, candidate.version, candidate.link) - self.candidates = candidates - self._manager: InstallManager | None = None + return candidates def should_install_editables(self) -> bool: - if self.self_candidate is None: + """Return whether to add editables""" + if not self.install_self or "editables" in self.requested_candidates: return False + # As editables may be added by the backend, we need to check the metadata metadata = self.self_candidate.prepare(self.environment).metadata return any(req.startswith("editables") for req in metadata.requires or []) @@ -200,6 +208,8 @@ def compare_with_working_set(self) -> tuple[list[str], list[str], list[str]]: candidates = self.candidates.copy() to_update: set[str] = set() to_remove: set[str] = set() + locked_repository = self.environment.project.locked_repository + all_candidate_keys = list(locked_repository.all_candidates) for key, dist in working_set.items(): if key == self.self_key: @@ -209,7 +219,7 @@ def compare_with_working_set(self) -> tuple[list[str], list[str], list[str]]: if self._should_update(dist, can): to_update.add(key) elif ( - self.only_keep or self.clean and key not in self.all_candidate_keys + self.only_keep or self.clean and key not in all_candidate_keys ) and key not in self.SEQUENTIAL_PACKAGES: # Remove package only if it is not required by any group # Packages for packaging will never be removed @@ -371,7 +381,7 @@ def update_progress(future: Future | DummyFuture, kind: str, key: str) -> None: errors.extend([f"{kind} [success]{key}[/] failed:\n", *traceback.format_exception(*exc_info)]) # get rich progress and live handler to deal with multiple spinners - with self.ui.logging("install"), self.ui.make_progress( + with self.ui.make_progress( " ", SpinnerColumn(termui.SPINNER, speed=1, style="primary"), "{task.description}",