Exemple #1
0
 def handle(self, project: Project, options: argparse.Namespace) -> None:
     if options.env:
         venv = next(
             (venv
              for key, venv in iter_venvs(project) if key == options.env),
             None)
         if not venv:
             project.core.ui.echo(
                 termui.yellow(
                     f"No virtualenv with key {options.env} is found"),
                 err=True,
             )
             raise SystemExit(1)
     else:
         # Use what is saved in .pdm.toml
         interpreter = project.python_executable
         if is_venv_python(interpreter):
             venv = Path(interpreter).parent.parent
         else:
             project.core.ui.echo(
                 termui.yellow(
                     f"Can't activate a non-venv Python{interpreter}, "
                     "you can specify one with pdm venv activate <env_name>"
                 ))
             raise SystemExit(1)
     project.core.ui.echo(self.get_activate_command(venv))
Exemple #2
0
    def _list_config(self, project: Project,
                     options: argparse.Namespace) -> None:
        ui = project.core.ui
        ui.echo("Home configuration ({}):".format(
            project.global_config._config_file))
        with ui.indent("  "):
            for key in sorted(project.global_config):
                ui.echo(
                    termui.yellow(
                        "# " +
                        project.global_config._config_map[key].description),
                    verbosity=termui.DETAIL,
                )
                ui.echo(f"{termui.cyan(key)} = {project.global_config[key]}")

        ui.echo()
        ui.echo("Project configuration ({}):".format(
            project.project_config._config_file))
        with ui.indent("  "):
            for key in sorted(project.project_config):
                ui.echo(
                    termui.yellow(
                        "# " +
                        project.project_config._config_map[key].description),
                    verbosity=termui.DETAIL,
                )
                ui.echo(f"{termui.cyan(key)} = {project.project_config[key]}")
Exemple #3
0
def ask_for_import(project: Project) -> None:
    """Show possible importable files and ask user to decide"""
    importable_files = list(find_importable_files(project))
    if not importable_files:
        return
    project.core.ui.echo(
        termui.cyan("Found following files from other formats that you may import:")
    )
    for i, (key, filepath) in enumerate(importable_files):
        project.core.ui.echo(f"{i}. {termui.green(filepath.as_posix())} ({key})")
    project.core.ui.echo(
        "{}. {}".format(
            len(importable_files),
            termui.yellow("don't do anything, I will import later."),
        )
    )
    choice = click.prompt(
        "Please select:",
        type=click.Choice([str(i) for i in range(len(importable_files) + 1)]),
        show_default=False,
    )
    if int(choice) == len(importable_files):
        return
    key, filepath = importable_files[int(choice)]
    do_import(project, str(filepath), key)
Exemple #4
0
    def handle(self, project: Project, options: argparse.Namespace) -> None:

        package = options.package
        req = parse_requirement(package)
        repository = project.get_repository()
        # reverse the result so that latest is at first.
        matches = repository.find_candidates(
            req, project.environment.python_requires, True
        )
        latest = next(iter(matches), None)
        if not latest:
            project.core.ui.echo(
                termui.yellow(f"No match found for the package {package!r}"), err=True
            )
            return
        latest_stable = next(filter(filter_stable, matches), None)
        installed = project.environment.get_working_set().get(package)

        metadata = latest.get_metadata()
        assert metadata
        if metadata._legacy:
            result = ProjectInfo(dict(metadata._legacy.items()), True)
        else:
            result = ProjectInfo(dict(metadata._data), False)
        if latest_stable:
            result.latest_stable_version = str(latest_stable.version)
        if installed:
            result.installed_version = str(installed.version)

        project.core.ui.display_columns(list(result.generate_rows()))
Exemple #5
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!")
Exemple #6
0
    def __setitem__(self, key: str, value: Any) -> None:
        if key not in self._config_map and key not in self.deprecated:
            raise NoConfigError(key)
        config_key = self.deprecated.get(key, key)
        config = self._config_map[config_key]
        if not self.is_global and config.global_only:
            raise ValueError(
                f"Config item '{key}' is not allowed to set in project config."
            )

        value = config.coerce(value)
        env_var = config.env_var
        if env_var is not None and env_var in os.environ:
            click.echo(
                termui.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "the value set won't take effect.".format(env_var)
                )
            )
        self._data[config_key] = value
        self._file_data[config_key] = value
        if config.replace:
            self._data.pop(config.replace, None)
            self._file_data.pop(config.replace, None)
        self._save_config()
Exemple #7
0
    def format_help(self) -> str:
        formatter = self._get_formatter()

        if getattr(self, "is_root", False):
            banner = (
                cfonts.render(
                    "PDM",
                    font="slick",
                    gradient=["bright_red", "bright_green"],
                    space=False,
                )
                + "\n"
            )
            formatter._add_item(lambda x: x, [banner])
            self._positionals.title = "Commands"
        self._optionals.title = "Options"
        # description
        formatter.add_text(self.description)

        # usage
        formatter.add_usage(
            self.usage,
            self._actions,
            self._mutually_exclusive_groups,
            prefix=termui.yellow("Usage", bold=True) + ": ",
        )

        # positionals, optionals and user-defined groups
        for action_group in self._action_groups:
            formatter.start_section(
                termui.yellow(action_group.title, bold=True)
                if action_group.title
                else None
            )
            formatter.add_text(action_group.description)
            formatter.add_arguments(action_group._group_actions)
            formatter.end_section()

        # epilog
        formatter.add_text(self.epilog)
        # determine help from format above
        return formatter.format_help()
Exemple #8
0
 def handle(self, project: Project, options: argparse.Namespace) -> None:
     project.core.ui.echo("Virtualenvs created with this project:")
     for ident, venv in iter_venvs(project):
         if ident == options.env:
             if options.yes or click.confirm(
                     termui.yellow(f"Will remove: {venv}, continue?")):
                 shutil.rmtree(venv)
                 if (project.project_config.get("python.path")
                         and Path(project.project_config["python.path"]
                                  ).parent.parent == venv):
                     del project.project_config["python.path"]
                 project.core.ui.echo("Removed successfully!")
             break
     else:
         project.core.ui.echo(
             termui.yellow(
                 f"No virtualenv with key {options.env} is found"),
             err=True,
         )
         raise SystemExit(1)
Exemple #9
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]}")
Exemple #10
0
 def _format_usage(
     self,
     usage: str,
     actions: Iterable[Action],
     groups: Iterable[_ArgumentGroup],
     prefix: str | None,
 ) -> str:
     if prefix is None:
         prefix = "Usage: "
     result = super()._format_usage(usage, actions, groups, prefix)
     if prefix:
         return result.replace(prefix, termui.yellow(prefix, bold=True))
     return result
Exemple #11
0
def migrate_pyproject(project: Project):
    """Migrate the legacy pyproject format to PEP 621"""

    if (not project.pyproject_file.exists()
            or not FORMATS["legacy"].check_fingerprint(project,
                                                       project.pyproject_file)
            or "project" in project.pyproject):
        return

    project.core.ui.echo(
        termui.yellow(
            "Legacy [tool.pdm] metadata detected, migrating to PEP 621..."),
        err=True,
    )
    do_import(project, project.pyproject_file, "legacy")
    project.core.ui.echo(
        termui.green("pyproject.toml") + termui.yellow(
            " has been migrated to PEP 621 successfully. "
            "Now you can safely delete the legacy metadata under [tool.pdm] table."
        ),
        err=True,
    )
Exemple #12
0
def migrate_pyproject(project: Project):
    """Migrate the legacy pyproject format to PEP 621"""

    if project.pyproject and "project" in project.pyproject:
        pyproject = project.pyproject
        settings = {}
        updated_fields = []
        for field in ("includes", "excludes", "build", "package-dir"):
            if field in pyproject["project"]:
                updated_fields.append(field)
                settings[field] = pyproject["project"][field]
                del pyproject["project"][field]
        if "dev-dependencies" in pyproject["project"]:
            if pyproject["project"]["dev-dependencies"]:
                settings["dev-dependencies"] = {
                    "dev": pyproject["project"]["dev-dependencies"]
                }
            del pyproject["project"]["dev-dependencies"]
            updated_fields.append("dev-dependencies")
        if updated_fields:
            if "tool" not in pyproject or "pdm" not in pyproject["tool"]:
                setdefault(pyproject, "tool", {})["pdm"] = tomlkit.table()
            pyproject["tool"]["pdm"].update(settings)
            project.pyproject = pyproject
            project.write_pyproject()
            project.core.ui.echo(
                f"{termui.yellow('[AUTO-MIGRATION]')} These fields are moved from "
                f"[project] to [tool.pdm] table: {updated_fields}",
                err=True,
            )
        return

    if not project.pyproject_file.exists() or not FORMATS["legacy"].check_fingerprint(
        project, project.pyproject_file
    ):
        return

    project.core.ui.echo(
        f"{termui.yellow('[AUTO-MIGRATION]')} Legacy pdm 0.x metadata detected, "
        "migrating to PEP 621...",
        err=True,
    )
    do_import(project, project.pyproject_file, "legacy")
    project.core.ui.echo(
        termui.green("pyproject.toml")
        + termui.yellow(
            " has been migrated to PEP 621 successfully. "
            "Now you can safely delete the legacy metadata under [tool.pdm] table."
        ),
        err=True,
    )
Exemple #13
0
 def __delitem__(self, key: str) -> None:
     self._data.pop(key, None)
     try:
         del self._file_data[key]
     except KeyError:
         pass
     else:
         env_var = self._config_map[key].env_var
         if env_var is not None and env_var in os.environ:
             click.echo(
                 termui.yellow(
                     "WARNING: the config is shadowed by env var '{}', "
                     "set value won't take effect.".format(env_var)))
         self._save_config()
Exemple #14
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)
Exemple #15
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)
Exemple #16
0
def do_list(
    project: Project,
    graph: bool = False,
    reverse: bool = False,
    freeze: bool = False,
    json: bool = False,
) -> None:
    """Display a list of packages installed in the local packages directory."""
    from pdm.cli.utils import build_dependency_graph, format_dependency_graph

    check_project_file(project)
    working_set = project.environment.get_working_set()
    if graph:
        dep_graph = build_dependency_graph(
            working_set, project.environment.marker_environment
        )
        project.core.ui.echo(
            format_dependency_graph(project, dep_graph, reverse=reverse, json=json)
        )
    else:
        if reverse:
            raise PdmUsageError("--reverse must be used with --graph")
        if json:
            raise PdmUsageError("--json must be used with --graph")
        if freeze:
            reqs = sorted(
                (
                    Requirement.from_dist(dist)
                    .as_line()
                    .replace(
                        "${PROJECT_ROOT}",
                        project.root.absolute().as_posix().lstrip("/"),
                    )
                    for dist in sorted(
                        working_set.values(), key=lambda d: d.metadata["Name"]
                    )
                ),
                key=lambda x: x.lower(),
            )
            project.core.ui.echo("\n".join(reqs))
            return
        rows = [
            (termui.green(k, bold=True), termui.yellow(v.version), get_dist_location(v))
            for k, v in sorted(working_set.items())
        ]
        project.core.ui.display_columns(rows, ["Package", "Version", "Location"])
Exemple #17
0
    def __setitem__(self, key: str, value: Any) -> None:
        if key not in self._config_map:
            raise NoConfigError(key)
        if not self.is_global and self._config_map[key].global_only:
            raise ValueError(
                f"Config item '{key}' is not allowed to set in project config."
            )

        value = self._config_map[key].coerce(value)
        env_var = self._config_map[key].env_var
        if env_var is not None and env_var in os.environ:
            click.echo(
                termui.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "the value set won't take effect.".format(env_var)))
        self._data[key] = value
        self._file_data[key] = value
        self._save_config()
Exemple #18
0
    def search(self, query: str) -> SearchResult:
        pypi_simple = self.sources[0]["url"].rstrip("/")
        results = []

        if pypi_simple.endswith("/simple"):
            search_url = pypi_simple[:-6] + "search"
        else:
            search_url = pypi_simple + "/search"

        with self.environment.get_finder() as finder:
            session = finder.session
            resp = session.get(search_url, params={"q": query})
            if resp.status_code == 404:
                self.environment.project.core.ui.echo(
                    termui.yellow(
                        f"{pypi_simple!r} doesn't support '/search' endpoint, fallback "
                        f"to {self.DEFAULT_INDEX_URL!r} now.\n"
                        "This may take longer depending on your network condition."
                    ),
                    err=True,
                )
                resp = session.get(f"{self.DEFAULT_INDEX_URL}/search",
                                   params={"q": query})
            resp.raise_for_status()
            content = parse(resp.content, namespaceHTMLElements=False)

        for result in content.findall(".//*[@class='package-snippet']"):
            name = result.find("h3/*[@class='package-snippet__name']").text
            version = result.find(
                "h3/*[@class='package-snippet__version']").text

            if not name or not version:
                continue

            description = result.find(
                "p[@class='package-snippet__description']").text
            if not description:
                description = ""

            result = Package(name, version, description)
            results.append(result)

        return results
Exemple #19
0
    def __delitem__(self, key: str) -> None:
        config_key = self.deprecated.get(key, key)
        config = self._config_map[config_key]
        self._data.pop(config_key, None)
        self._file_data.pop(config_key, None)
        if self.is_global and config.should_show():
            self._data[config_key] = config.default
        if config.replace:
            self._data.pop(config.replace, None)
            self._file_data.pop(config.replace, None)

        env_var = config.env_var
        if env_var is not None and env_var in os.environ:
            click.echo(
                termui.yellow(
                    "WARNING: the config is shadowed by env var '{}', "
                    "set value won't take effect.".format(env_var)
                )
            )
        self._save_config()
Exemple #20
0
def print_results(
    ui: termui.UI,
    hits: SearchResult,
    working_set: WorkingSet,
    terminal_width: Optional[int] = None,
) -> None:
    if not hits:
        return
    name_column_width = (
        max([len(hit.name) + len(hit.version or "") for hit in hits]) + 4
    )

    for hit in hits:
        name = hit.name
        summary = hit.summary or ""
        latest = hit.version or ""
        if terminal_width is not None:
            target_width = terminal_width - name_column_width - 5
            if target_width > 10:
                # wrap and indent summary to fit terminal
                summary = textwrap.wrap(summary, target_width)
                summary = ("\n" + " " * (name_column_width + 2)).join(summary)
        current_width = len(name) + len(latest) + 4
        spaces = " " * (name_column_width - current_width)
        line = "{name} ({latest}){spaces} - {summary}".format(
            name=termui.green(name, bold=True),
            latest=termui.yellow(latest),
            spaces=spaces,
            summary=summary,
        )
        try:
            ui.echo(line)
            if safe_name(name).lower() in working_set:
                dist = working_set[safe_name(name).lower()]
                if dist.version == latest:
                    ui.echo("  INSTALLED: %s (latest)" % dist.version)
                else:
                    ui.echo("  INSTALLED: %s" % dist.version)
                    ui.echo("  LATEST:    %s" % latest)
        except UnicodeEncodeError:
            pass
Exemple #21
0
    def handle(self, project: Project, options: argparse.Namespace) -> None:
        package = options.package
        if package:
            req = parse_requirement(package)
            repository = project.get_repository()
            # reverse the result so that latest is at first.
            matches = repository.find_candidates(
                req, project.environment.python_requires, True
            )
            latest = next(iter(matches), None)
            if not latest:
                project.core.ui.echo(
                    termui.yellow(f"No match found for the package {package!r}"),
                    err=True,
                )
                return
            latest_stable = next(filter(filter_stable, matches), None)

            metadata = latest.metadata
        else:
            if not project.meta.name:
                raise PdmUsageError("This project is not a package")
            metadata = project.meta
            package = normalize_name(metadata.name)
            latest_stable = None
        assert metadata
        project_info = ProjectInfo(metadata)
        if any(getattr(options, key, None) for key in self.metadata_keys):
            for key in self.metadata_keys:
                if getattr(options, key, None):
                    project.core.ui.echo(project_info[key])
            return

        installed = project.environment.get_working_set().get(package)
        if latest_stable:
            project_info.latest_stable_version = str(latest_stable.version)
        if installed:
            project_info.installed_version = str(installed.version)
        project.core.ui.display_columns(list(project_info.generate_rows()))
Exemple #22
0
 def start_section(self, heading: str | None) -> None:
     return super().start_section(
         termui.yellow(heading.title() if heading else heading, bold=True))
Exemple #23
0
def format_dist(dist: Distribution) -> str:
    formatter = "{version}{path}"
    path = ""
    if is_dist_editable(dist):
        path = f" (-e {dist.location})"
    return formatter.format(version=termui.yellow(dist.version), path=path)