예제 #1
0
def do_use(project: Project, python: str, first: bool = False) -> None:
    """Use the specified python version and save in project config.
    The python can be a version string or interpreter path.
    """
    import pythonfinder

    if python and not all(c.isdigit() for c in python.split(".")):
        if Path(python).exists():
            python_path = Path(python).absolute().as_posix()
        else:
            python_path = shutil.which(python)
        if not python_path:
            raise NoPythonVersion(f"{python} is not a valid Python.")
        python_version = get_python_version(python_path, True)
    else:
        finder = pythonfinder.Finder()
        pythons = []
        args = [int(v) for v in python.split(".") if v != ""]
        for i, entry in enumerate(finder.find_all_python_versions(*args)):
            python_version = get_python_version(entry.path.as_posix(), True)
            pythons.append((entry.path.as_posix(), python_version))
        if not pythons:
            raise NoPythonVersion(
                f"Python {python} is not available on the system.")

        if not first and len(pythons) > 1:
            for i, (path, python_version) in enumerate(pythons):
                stream.echo(f"{i}. {stream.green(path)} ({python_version})")
            selection = click.prompt(
                "Please select:",
                type=click.Choice([str(i) for i in range(len(pythons))]),
                default="0",
                show_choices=False,
            )
        else:
            selection = 0
        python_path, python_version = pythons[int(selection)]

    if not project.python_requires.contains(python_version):
        raise NoPythonVersion("The target Python version {} doesn't satisfy "
                              "the Python requirement: {}".format(
                                  python_version, project.python_requires))
    stream.echo("Using Python interpreter: {} ({})".format(
        stream.green(python_path), python_version))
    old_path = project.config.get("python.path")
    new_path = python_path
    project.project_config["python.path"] = Path(new_path).as_posix()
    if old_path and Path(old_path) != Path(new_path) and not project.is_global:
        stream.echo(stream.cyan("Updating executable scripts..."))
        project.environment.update_shebangs(new_path)
예제 #2
0
def init(project):
    """Initialize a pyproject.toml for PDM."""
    python = click.prompt("Please enter the Python interpreter to use")
    actions.do_use(project, python)

    if project.pyproject_file.exists():
        context.io.echo("{}".format(
            context.io.cyan("pyproject.toml already exists, update it now.")))
    else:
        context.io.echo("{}".format(
            context.io.cyan("Creating a pyproject.toml for PDM...")))
    name = click.prompt(f"Project name", default=project.root.name)
    version = click.prompt("Project version", default="0.0.0")
    license = click.prompt("License(SPDX name)", default="MIT")

    git_user, git_email = get_user_email_from_git()
    author = click.prompt(f"Author name", default=git_user)
    email = click.prompt(f"Author email", default=git_email)
    python_version = ".".join(
        map(str,
            get_python_version(project.environment.python_executable)[:2]))
    python_requires = click.prompt("Python requires('*' to allow any)",
                                   default=f">={python_version}")

    actions.do_init(project, name, version, license, author, email,
                    python_requires)
예제 #3
0
파일: init.py 프로젝트: skyoo2003/pdm
    def handle(self, project: Project, options: argparse.Namespace) -> None:
        if project.pyproject_file.exists():
            stream.echo("{}".format(
                stream.cyan("pyproject.toml already exists, update it now.")))
        else:
            stream.echo("{}".format(
                stream.cyan("Creating a pyproject.toml for PDM...")))
        python = click.prompt("Please enter the Python interpreter to use",
                              default="",
                              show_default=False)
        actions.do_use(project, python)
        name = click.prompt("Project name", default=project.root.name)
        version = click.prompt("Project version", default="0.0.0")
        license = click.prompt("License(SPDX name)", default="MIT")

        git_user, git_email = get_user_email_from_git()
        author = click.prompt("Author name", default=git_user)
        email = click.prompt("Author email", default=git_email)
        python_version = ".".join(
            map(str,
                get_python_version(project.environment.python_executable)[:2]))
        python_requires = click.prompt("Python requires('*' to allow any)",
                                       default=f">={python_version}")

        actions.do_init(project, name, version, license, author, email,
                        python_requires)
        actions.ask_for_import(project)
예제 #4
0
파일: actions.py 프로젝트: stacklens/pdm
def do_use(project: Project, python: str) -> None:
    """Use the specified python version and save in project config.
    The python can be a version string or interpreter path.
    """
    if Path(python).is_absolute():
        python_path = python
    else:
        python_path = shutil.which(python)
        if not python_path:
            finder = pythonfinder.Finder()
            try:
                python_path = finder.find_python_version(
                    python).path.as_posix()
            except AttributeError:
                raise NoPythonVersion(
                    f"Python {python} is not found on the system.")

    python_version = get_python_version(python_path, True)
    if not project.python_requires.contains(python_version):
        raise NoPythonVersion("The target Python version {} doesn't satisfy "
                              "the Python requirement: {}".format(
                                  python_version, project.python_requires))
    context.io.echo("Using Python interpreter: {} ({})".format(
        context.io.green(python_path), python_version))

    project.config["python"] = Path(python_path).as_posix()
    project.config.save_config()
예제 #5
0
 def _get_pip_command(self) -> List[str]:
     """Get a pip command that has pip installed.
     E.g: ['python', '-m', 'pip']
     """
     python_version = get_python_version(self.executable)
     proc = subprocess.run([self.executable, "-m", "pip", "--version"],
                           capture_output=True)
     if proc.returncode == 0:
         # The pip has already been installed with the executable, just use it
         return [self.executable, "-m", "pip"]
     if python_version[0] == 3:
         # Use the ensurepip to provision one.
         try:
             self.subprocess_runner([
                 self.executable, "-Im", "ensurepip", "--upgrade",
                 "--default-pip"
             ])
         except BuildError:
             pass
         else:
             return [self.executable, "-m", "pip"]
     # Otherwise, download a pip wheel from the Internet.
     pip_wheel = self._env.project.cache_dir / "pip.whl"
     if not pip_wheel.is_file():
         _download_pip_wheel(pip_wheel)
     return [self.executable, str(pip_wheel / "pip")]
예제 #6
0
def test_init_command(project_no_init, invoke, mocker):
    mocker.patch(
        "pdm.cli.commands.get_user_email_from_git",
        return_value=("Testing", "*****@*****.**"),
    )
    do_init = mocker.patch.object(actions, "do_init")
    result = invoke(["init"],
                    input="python\ntest-project\n\n\n\n\n\n",
                    obj=project_no_init)
    print(result.output)
    assert result.exit_code == 0
    python_version = ".".join(
        map(
            str,
            get_python_version(
                project_no_init.environment.python_executable)[:2]))
    do_init.assert_called_with(
        project_no_init,
        "test-project",
        "0.0.0",
        "MIT",
        "Testing",
        "*****@*****.**",
        f">={python_version}",
    )
예제 #7
0
파일: actions.py 프로젝트: stacklens/pdm
def do_info(
    project: Project,
    python: bool = False,
    show_project: bool = False,
    env: bool = False,
) -> None:
    """Show project information."""
    python_path = project.environment.python_executable
    python_version = get_python_version(python_path)
    if not python and not show_project and not env:
        rows = [
            (
                context.io.cyan("Python Interpreter:", bold=True),
                python_path + f" ({python_version})",
            ),
            (context.io.cyan("Project Root:",
                             bold=True), project.root.as_posix()),
        ]
        context.io.display_columns(rows)
        return

    if python:
        context.io.echo(python_path)
    if show_project:
        context.io.echo(project.root.as_posix())
    if env:
        context.io.echo(
            json.dumps(project.environment.marker_environment, indent=2))
예제 #8
0
 def which(self, command: str) -> str:
     """Get the full path of the given executable against this environment."""
     if not os.path.isabs(command) and command.startswith("python"):
         python = os.path.splitext(command)[0]
         version = python[6:]
         this_version = get_python_version(self.python_executable, True)
         if not version or this_version.startswith(version):
             return self.python_executable
     # Fallback to use shutil.which to find the executable
     return shutil.which(command, path=os.getenv("PATH"))
예제 #9
0
파일: environment.py 프로젝트: pcskys/pdm
 def packages_path(self) -> Path:
     """The local packages path."""
     pypackages = (
         self.project.root / "__pypackages__" /
         ".".join(map(str,
                      get_python_version(self.python_executable)[:2])))
     scripts = "Scripts" if os.name == "nt" else "bin"
     for subdir in [scripts, "include", "lib"]:
         pypackages.joinpath(subdir).mkdir(exist_ok=True, parents=True)
     return pypackages
예제 #10
0
파일: environment.py 프로젝트: pcskys/pdm
    def python_executable(self) -> str:
        """Get the Python interpreter path."""
        config = self.project.config
        if config.get("python.path"):
            return config["python.path"]
        if PYENV_INSTALLED and config.get("python.use_pyenv", True):
            return os.path.join(PYENV_ROOT, "shims", "python")
        if "VIRTUAL_ENV" in os.environ:
            stream.echo(
                "An activated virtualenv is detected, reuse the interpreter now.",
                err=True,
                verbosity=stream.DETAIL,
            )
            return get_venv_python()

        # First try what `python` refers to.
        path = shutil.which("python")
        version = None
        if path:
            version = get_python_version(path, True)
        if not version or not self.python_requires.contains(version):
            finder = Finder()
            for python in finder.find_all_python_versions():
                version = get_python_version(python.path.as_posix(), True)
                if self.python_requires.contains(version):
                    path = python.path.as_posix()
                    break
            else:
                version = ".".join(map(str, sys.version_info[:3]))
                if self.python_requires.contains(version):
                    path = sys.executable
        if path:
            stream.echo("Using Python interpreter: {} ({})".format(
                stream.green(path), version))
            self.project.project_config["python.path"] = Path(path).as_posix()
            return path
        raise NoPythonVersion(
            "No Python that satisfies {} is found on the system.".format(
                self.python_requires))
예제 #11
0
    def python_executable(self) -> str:
        """Get the Python interpreter path."""
        path = None
        if self.config["python"]:
            path = self.config["python"]
            try:
                get_python_version(path)
                return path
            except Exception:
                pass

        path = None
        version = None
        # First try what `python` refers to.
        path = shutil.which("python")
        if path:
            version = get_python_version(path, True)
        else:
            finder = Finder()
            for python in finder.find_all_python_versions():
                version = ".".join(
                    map(str, get_python_version(python.path.as_posix())))
                if self.python_requires.contains(version):
                    path = python.path.as_posix()
                    break
            else:
                version = ".".join(map(str, sys.version_info[:3]))
                if self.python_requires.contains(version):
                    path = sys.executable
        if path:
            context.io.echo("Using Python interpreter: {} ({})".format(
                context.io.green(path), version))
            self.config["python"] = Path(path).as_posix()
            self.config.save_config()
            return path
        raise NoPythonVersion(
            "No Python that satisfies {} is found on the system.".format(
                self.python_requires))
예제 #12
0
 def environment(self) -> Environment:
     if self.is_global:
         env = GlobalEnvironment(self)
         # Rewrite global project's python requires to be
         # compatible with the exact version
         env.python_requires = PySpecSet(
             "==" + get_python_version(env.python_executable, True))
         return env
     if self.config["use_venv"]:
         venv_python = get_venv_python(self.root)
         if venv_python:
             self.project_config["python.path"] = venv_python
             return GlobalEnvironment(self)
     return Environment(self)
예제 #13
0
    def python_executable(self) -> str:
        """Get the Python interpreter path."""
        if self.config.get("python.path"):
            path = self.config["python.path"]
            try:
                get_python_version(path)
                return path
            except Exception:
                pass
        if PYENV_INSTALLED and self.config.get("python.use_pyenv", True):
            return os.path.join(PYENV_ROOT, "shims", "python")

        # First try what `python` refers to.
        path = shutil.which("python")
        version = None
        if path:
            version = get_python_version(path, True)
        if not version or not self.python_requires.contains(version):
            finder = Finder()
            for python in finder.find_all_python_versions():
                version = get_python_version(python.path.as_posix(), True)
                if self.python_requires.contains(version):
                    path = python.path.as_posix()
                    break
            else:
                version = ".".join(map(str, sys.version_info[:3]))
                if self.python_requires.contains(version):
                    path = sys.executable
        if path:
            context.io.echo("Using Python interpreter: {} ({})".format(
                context.io.green(path), version))
            self.config["python.path"] = Path(path).as_posix()
            self.config.save_config()
            return path
        raise NoPythonVersion(
            "No Python that satisfies {} is found on the system.".format(
                self.python_requires))
예제 #14
0
 def which(self, command: str) -> str:
     """Get the full path of the given executable against this environment."""
     if not os.path.isabs(command) and command.startswith("python"):
         python = os.path.splitext(command)[0]
         version = python[6:]
         this_version = get_python_version(self.python_executable, True)
         if not version or this_version.startswith(version):
             return self.python_executable
     # Fallback to use shutil.which to find the executable
     this_path = self.get_paths()["scripts"]
     python_root = os.path.dirname(self.python_executable)
     new_path = os.pathsep.join(
         [python_root, this_path,
          os.getenv("PATH", "")])
     return shutil.which(command, path=new_path)
예제 #15
0
    def get_finder(
        self,
        sources: Optional[List[Source]] = None,
        ignore_requires_python: bool = False,
    ) -> shims.PackageFinder:
        """Return the package finder of given index sources.

        :param sources: a list of sources the finder should search in.
        :param ignore_requires_python: whether to ignore the python version constraint.
        """
        sources = sources or []
        python_version = get_python_version(self.python_executable)[:2]
        finder = get_finder(
            sources,
            context.cache_dir.as_posix(),
            python_version,
            ignore_requires_python,
        )
        yield finder
        finder.session.close()
예제 #16
0
파일: environment.py 프로젝트: pcskys/pdm
 def __init__(self, project: Project) -> None:
     super().__init__(project)
     self.python_requires = PySpecSet(
         "==" + get_python_version(self.python_executable, True))
예제 #17
0
    def build(
        self,
        ireq: pip_shims.InstallRequirement,
        hashes: Optional[Dict[str, str]] = None,
        allow_all: bool = True,
    ) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :param allow_all: Allow building incompatible wheels.
        :returns: The full path of the built artifact.
        """
        kwargs = self._make_building_args(ireq)
        wheel_cache = self.project.make_wheel_cache()
        with self.get_finder() as finder:
            with allow_all_wheels(allow_all):
                # temporarily allow all wheels to get a link.
                populate_link(finder, ireq, False)
                if hashes is None:
                    cache_entry = wheel_cache.get_cache_entry(
                        ireq.link,
                        ireq.req.project_name,
                        pip_shims.get_supported(version="".join(
                            map(str,
                                get_python_version(self.python_executable)
                                [:2]))),
                    )
                    if cache_entry is not None:
                        stream.logger.debug("Using cached wheel link: %s",
                                            cache_entry.link)
                        ireq.link = cache_entry.link
            if not ireq.editable and not ireq.req.name:
                ireq.source_dir = kwargs["build_dir"]
            else:
                ireq.ensure_has_source_dir(kwargs["build_dir"])

            download_dir = kwargs["download_dir"]
            only_download = False
            if ireq.link.is_wheel:
                download_dir = kwargs["wheel_download_dir"]
                only_download = True
            if hashes:
                ireq.hash_options = convert_hashes(hashes)
            if not (ireq.editable and ireq.req.is_local_dir):
                downloader = pip_shims.Downloader(finder.session, "off")
                downloaded = pip_shims.unpack_url(
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    download_dir,
                    ireq.hashes(False),
                )
                # Preserve the downloaded file so that it won't be cleared.
                if downloaded and only_download:
                    try:
                        shutil.copy(downloaded.path, download_dir)
                    except shutil.SameFileError:
                        pass

            if ireq.link.is_wheel:
                # If the file is a wheel, should be already present under download dir.
                return (self.project.cache("wheels") /
                        ireq.link.filename).as_posix()
            else:
                # Check the built wheel cache again after hashes are resolved.
                cache_entry = wheel_cache.get_cache_entry(
                    ireq.link,
                    ireq.req.project_name,
                    pip_shims.get_supported(version="".join(
                        map(str,
                            get_python_version(self.python_executable)[:2]))),
                )
                if cache_entry is not None:
                    stream.logger.debug("Using cached wheel link: %s",
                                        cache_entry.link)
                    return cache_entry.link.file_path

            # Otherwise, now all source is prepared, build it.
            with EnvBuilder(ireq.unpacked_source_directory, self) as builder:
                if ireq.editable:
                    ret = builder.build_egg_info(kwargs["build_dir"])
                    ireq.metadata_directory = ret
                else:
                    should_cache = False
                    if ireq.link.is_vcs:
                        vcs = pip_shims.VcsSupport()
                        vcs_backend = vcs.get_backend_for_scheme(
                            ireq.link.scheme)
                        if vcs_backend.is_immutable_rev_checkout(
                                ireq.link.url, ireq.source_dir):
                            should_cache = True
                    else:
                        base, _ = ireq.link.splitext()
                        if _egg_info_re.search(base) is not None:
                            # Determine whether the string looks like an egg_info.
                            should_cache = True
                    output_dir = (wheel_cache.get_path_for_link(ireq.link)
                                  if should_cache else kwargs["build_dir"])
                    if not os.path.exists(output_dir):
                        os.makedirs(output_dir, exist_ok=True)
                    ret = builder.build_wheel(output_dir)
            return ret
예제 #18
0
 def get_working_set(self) -> WorkingSet:
     """Get the working set based on local packages directory."""
     paths = self.get_paths()
     return WorkingSet([paths["platlib"]],
                       python=get_python_version(self.python_executable))