def _list_config(self, project: Project, options: argparse.Namespace) -> None: stream.echo( "Home configuration ({}):".format(project.global_config._config_file) ) with stream.indent(" "): for key in sorted(project.global_config): stream.echo( stream.yellow( "# " + project.global_config._config_map[key].description ), verbosity=stream.DETAIL, ) stream.echo(f"{stream.cyan(key)} = {project.global_config[key]}") stream.echo() stream.echo( "Project configuration ({}):".format(project.project_config._config_file) ) with stream.indent(" "): for key in sorted(project.project_config): stream.echo( stream.yellow( "# " + project.project_config._config_map[key].description ), verbosity=stream.DETAIL, ) stream.echo(f"{stream.cyan(key)} = {project.project_config[key]}")
def synchronize(self, clean: bool = True, dry_run: bool = False) -> None: """Synchronize the working set with pinned candidates. :param clean: Whether to remove unneeded packages, defaults to True. :param dry_run: If set to True, only prints actions without actually do them. """ to_add, to_update, to_remove = self.compare_with_working_set() if not clean: to_remove = [] if not any([to_add, to_update, to_remove]): stream.echo( stream.yellow( "All packages are synced to date, nothing to do.")) if not dry_run: with stream.logging("install"): self.update_project_egg_info() return to_do = {"remove": to_remove, "update": to_update, "add": to_add} self._show_headline(to_do) if dry_run: self._show_summary(to_do) return handlers = { "add": self.install_candidate, "update": self.update_candidate, "remove": self.remove_distribution, } sequential_jobs = [] parallel_jobs = [] # Self package will be installed after all other dependencies are installed. install_self = None for kind in to_do: for key in to_do[kind]: if (key == self.environment.project.meta.name and self.environment.project.meta.project_name.lower()): install_self = (kind, key) elif key in self.SEQUENTIAL_PACKAGES: sequential_jobs.append((kind, key)) elif key in self.candidates and self.candidates[ key].req.editable: # Editable packages are installed sequentially. sequential_jobs.append((kind, key)) else: parallel_jobs.append((kind, key)) errors: List[str] = [] failed_jobs: List[Tuple[str, str]] = [] def update_progress(future, kind, key): if future.exception(): failed_jobs.append((kind, key)) error = future.exception() errors.extend([f"{kind} {stream.green(key)} failed:\n"] + traceback.format_exception( type(error), error, error.__traceback__)) with stream.logging("install"), self.environment.activate(): with stream.indent(" "): for job in sequential_jobs: kind, key = job handlers[kind](key) for i in range(self.retry_times + 1): with self.create_executor() as executor: for job in parallel_jobs: kind, key = job future = executor.submit(handlers[kind], key) future.add_done_callback( functools.partial(update_progress, kind=kind, key=key)) if not failed_jobs or i == self.retry_times: break parallel_jobs, failed_jobs = failed_jobs, [] errors.clear() stream.echo("Retry failed jobs") if errors: stream.echo(stream.red("\nERRORS:")) stream.echo("".join(errors), err=True) raise InstallationError( "Some package operations are not complete yet") if install_self: stream.echo("Installing the project as an editable package...") with stream.indent(" "): handlers[install_self[0]](install_self[1]) else: self.update_project_egg_info() stream.echo(f"\n{CELE} All complete!")