Beispiel #1
0
def format_package(
    graph: DirectedGraph,
    package: Package,
    required: str = "",
    prefix: str = "",
    visited: Optional[Set[str]] = None,
) -> str:
    """Format one package.

    :param graph: the dependency graph
    :param package: the package instance
    :param required: the version required by its parent
    :param prefix: prefix text for children
    :param visited: the visited package collection
    """
    if visited is None:
        visited = set()
    result = []
    version = (
        termui.red("[ not installed ]")
        if not package.version
        else termui.red(package.version)
        if required
        and required not in ("Any", "This project")
        and not SpecifierSet(required).contains(package.version)
        else termui.yellow(package.version)
    )
    if package.name in visited:
        version = termui.red("[circular]")
    required = f"[ required: {required} ]" if required else "[ Not required ]"
    result.append(f"{termui.green(package.name, bold=True)} {version} {required}\n")
    if package.name in visited:
        return "".join(result)
    visited.add(package.name)
    children = sorted(graph.iter_children(package), key=lambda p: p.name)
    for i, child in enumerate(children):
        is_last = i == len(children) - 1
        head = LAST_CHILD if is_last else NON_LAST_CHILD
        cur_prefix = LAST_PREFIX if is_last else NON_LAST_PREFIX
        required = str(package.requirements[child.name].specifier or "Any")
        result.append(
            prefix
            + head
            + format_package(
                graph, child, required, prefix + cur_prefix, visited.copy()
            )
        )
    return "".join(result)
Beispiel #2
0
def print_pep582_command(ui: termui.UI, shell: str = "AUTO"):
    """Print the export PYTHONPATH line to be evaluated by the shell."""
    import shellingham

    if os.name == "nt":
        try:
            set_env_in_reg("PYTHONPATH", PEP582_PATH)
        except PermissionError:
            ui.echo(
                termui.red(
                    "Permission denied, please run the terminal as administrator."
                ),
                err=True,
            )
        ui.echo(
            termui.green("The environment variable has been saved, "
                         "please restart the session to take effect."))
        return
    lib_path = PEP582_PATH.replace("'", "\\'")
    if shell == "AUTO":
        shell = shellingham.detect_shell()[0]
    shell = shell.lower()
    if shell in ("zsh", "bash"):
        result = f"export PYTHONPATH='{lib_path}':$PYTHONPATH"
    elif shell == "fish":
        result = f"set -x PYTHONPATH '{lib_path}' $PYTHONPATH"
    elif shell in ("tcsh", "csh"):
        result = f"setenv PYTHONPATH '{lib_path}':$PYTHONPATH"
    else:
        raise PdmUsageError(
            f"Unsupported shell: {shell}, please specify another shell "
            "via `--pep582 <SHELL>`")
    ui.echo(result)
Beispiel #3
0
    def handle(self, project: Project, options: argparse.Namespace) -> None:

        if not options.force:
            project.core.ui.echo(
                termui.red("The following Virtualenvs will be purged:")
            )
            for i, venv in enumerate(get_all_venvs(project)):
                project.core.ui.echo(f"{i}. {termui.green(venv[0])}")

        if not options.interactive and (
            options.force or click.confirm(termui.yellow("continue? "))
        ):
            self.del_all_venvs(project)

        if options.interactive:
            selection = click.prompt(
                "Please select",
                type=click.Choice(
                    [str(i) for i in range(len(list(get_all_venvs(project))))]
                    + ["all", "none"]
                ),
                default="none",
                show_choices=False,
            )

            if selection == "all":
                self.del_all_venvs(project)
            elif selection != "none":
                for i, venv in enumerate(get_all_venvs(project)):
                    if i == int(selection):
                        shutil.rmtree(venv[1])
                project.core.ui.echo("Purged successfully!")
Beispiel #4
0
def format_reverse_package(
    graph: DirectedGraph,
    package: Package,
    child: Optional[Package] = None,
    requires: str = "",
    prefix: str = "",
    visited: Optional[Set[str]] = None,
):
    """Format one package for output reverse dependency graph."""
    if visited is None:
        visited = set()
    result = []
    version = (
        termui.red("[ not installed ]")
        if not package.version
        else termui.yellow(package.version)
    )
    if package.name in visited:
        version = termui.red("[circular]")
    requires = (
        f"[ requires: {termui.red(requires)} ]"
        if requires not in ("Any", "")
        and child
        and child.version
        and not SpecifierSet(requires).contains(child.version)
        else ""
        if not requires
        else f"[ requires: {requires} ]"
    )
    result.append(f"{termui.green(package.name, bold=True)} {version} {requires}\n")
    if package.name in visited:
        return "".join(result)
    visited.add(package.name)
    parents = sorted(filter(None, graph.iter_parents(package)), key=lambda p: p.name)
    for i, parent in enumerate(parents):
        is_last = i == len(parents) - 1
        head = LAST_CHILD if is_last else NON_LAST_CHILD
        cur_prefix = LAST_PREFIX if is_last else NON_LAST_PREFIX
        requires = str(parent.requirements[package.name].specifier or "Any")
        result.append(
            prefix
            + head
            + format_reverse_package(
                graph, parent, package, requires, prefix + cur_prefix, visited.copy()
            )
        )
    return "".join(result)
Beispiel #5
0
 def _show_config(self, config: Config, ui: termui.UI) -> None:
     for key in sorted(config):
         config_item = config._config_map[key]
         deprecated = ""
         if config_item.replace and config_item.replace in config._data:
             deprecated = termui.red(
                 f"(deprecating: {config_item.replace})")
         ui.echo(
             termui.yellow("# " + config_item.description),
             verbosity=termui.DETAIL,
         )
         ui.echo(f"{termui.cyan(key)}{deprecated} = {config[key]}")
Beispiel #6
0
    def synchronize(self) -> None:
        """Synchronize the working set with pinned candidates."""
        to_add, to_update, to_remove = self.compare_with_working_set()
        to_do = {"remove": to_remove, "update": to_update, "add": to_add}

        if self.dry_run:
            self._show_summary(to_do)
            return

        self._show_headline(to_do)
        handlers = {
            "add": self.install_candidate,
            "update": self.update_candidate,
            "remove": self.remove_distribution,
        }
        sequential_jobs = []
        parallel_jobs = []

        for kind in to_do:
            for key in to_do[kind]:
                if 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: Union[Future, DummyFuture], kind: str,
                            key: str) -> None:
            error = future.exception()
            if error:
                exc_info = (type(error), error, error.__traceback__)
                termui.logger.exception("Error occurs: ", exc_info=exc_info)
                failed_jobs.append((kind, key))
                errors.extend([f"{kind} {termui.green(key)} failed:\n"] +
                              traceback.format_exception(*exc_info))

        with self.ui.logging("install"):
            with self.ui.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()
                    self.ui.echo("Retry failed jobs")

            if errors:
                self.ui.echo(termui.red("\nERRORS:"))
                self.ui.echo("".join(errors), err=True)
                raise InstallationError(
                    "Some package operations are not complete yet")

            if self.install_self:
                self_candidate = self.environment.project.make_self_candidate(
                    not self.no_editable)
                self_key = self.self_key
                assert self_key
                self.candidates[self_key] = self_candidate
                word = "a" if self.no_editable else "an editable"
                self.ui.echo(f"Installing the project as {word} package...")
                with self.ui.indent("  "):
                    if self_key in self.working_set:
                        self.update_candidate(self_key)
                    else:
                        self.install_candidate(self_key)

            self.ui.echo(f"\n{termui.Emoji.SUCC} All complete!")
Beispiel #7
0
    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 = []

        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.
        self_action = None
        self_key = self.self_key
        if self_key in self.candidates:
            self_action = "update" if self_key in self.working_set else "add"

        for kind in to_do:
            for key in to_do[kind]:
                if 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} {termui.green(key)} failed:\n"] +
                              traceback.format_exception(
                                  type(error), error, error.__traceback__))

        with self.ui.logging("install"), self.environment.activate():
            with self.ui.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()
                    self.ui.echo("Retry failed jobs")

            if errors:
                self.ui.echo(termui.red("\nERRORS:"))
                self.ui.echo("".join(errors), err=True)
                raise InstallationError(
                    "Some package operations are not complete yet")

            if self_action:
                self.ui.echo(
                    "Installing the project as an editable package...")
                with self.ui.indent("  "):
                    handlers[self_action](self_key)

            self.ui.echo(f"\n{termui.Emoji.SUCC} All complete!")