Beispiel #1
0
    def get_pip_version(self):  # type: () -> Version
        output = self.run("python", "-m", "pip", "--version").strip()
        m = re.match("pip (.+?)(?: from .+)?$", output)
        if not m:
            return Version.parse("0.0")

        return Version.parse(m.group(1))
Beispiel #2
0
    def _get_lock_data(self):  # type: () -> dict
        if not self._lock.exists():
            raise RuntimeError("No lockfile found. Unable to read locked packages")

        try:
            lock_data = self._lock.read()
        except TOMLKitError as e:
            raise RuntimeError("Unable to read the lock file ({}).".format(e))

        lock_version = Version.parse(lock_data["metadata"].get("lock-version", "1.0"))
        current_version = Version.parse(self._VERSION)
        accepted_versions = parse_constraint(
            "^{}".format(Version(current_version.major, current_version.minor))
        )
        lock_version_allowed = accepted_versions.allows(lock_version)
        if lock_version_allowed and current_version < lock_version:
            logger.warning(
                "The lock file might not be compatible with the current version of Poetry.\n"
                "Upgrade Poetry to ensure the lock file is read properly or, alternatively, "
                "regenerate the lock file with the `poetry lock` command."
            )
        elif not lock_version_allowed:
            raise RuntimeError(
                "The lock file is not compatible with the current version of Poetry.\n"
                "Upgrade Poetry to be able to read the lock file or, alternatively, "
                "regenerate the lock file with the `poetry lock` command."
            )

        return lock_data
Beispiel #3
0
    def __init__(self,
                 version_info=(3, 7, 0),
                 python_implementation="CPython",
                 platform="darwin",
                 os_name="posix",
                 is_venv=False,
                 pip_version="19.1",
                 **kwargs):
        super(MockEnv, self).__init__(**kwargs)

        self._version_info = version_info
        self._python_implementation = python_implementation
        self._platform = platform
        self._os_name = os_name
        self._is_venv = is_venv
        self._pip_version = Version.parse(pip_version)
Beispiel #4
0
    def get_pip_version(self):  # type: () -> Version
        from pip import __version__

        return Version.parse(__version__)
Beispiel #5
0
    def create_venv(self,
                    io,
                    name=None,
                    executable=None,
                    force=False
                    ):  # type: (IO, Optional[str], Optional[str], bool) -> Env
        if self._env is not None and not force:
            return self._env

        cwd = self._poetry.file.parent
        env = self.get(reload=True)
        if env.is_venv() and not force:
            # Already inside a virtualenv.
            return env

        create_venv = self._poetry.config.get("virtualenvs.create")
        root_venv = self._poetry.config.get("virtualenvs.in-project")

        venv_path = self._poetry.config.get("virtualenvs.path")
        if root_venv:
            venv_path = cwd / ".venv"
        elif venv_path is None:
            venv_path = Path(CACHE_DIR) / "virtualenvs"
        else:
            venv_path = Path(venv_path)

        if not name:
            name = self._poetry.package.name

        python_patch = ".".join([str(v) for v in sys.version_info[:3]])
        python_minor = ".".join([str(v) for v in sys.version_info[:2]])
        if executable:
            python_minor = decode(
                subprocess.check_output(
                    " ".join([
                        executable,
                        "-c",
                        "\"import sys; print('.'.join([str(s) for s in sys.version_info[:2]]))\"",
                    ]),
                    shell=True,
                ).strip())

        supported_python = self._poetry.package.python_constraint
        if not supported_python.allows(Version.parse(python_minor)):
            # The currently activated or chosen Python version
            # is not compatible with the Python constraint specified
            # for the project.
            # If an executable has been specified, we stop there
            # and notify the user of the incompatibility.
            # Otherwise, we try to find a compatible Python version.
            if executable:
                raise NoCompatiblePythonVersionFound(
                    self._poetry.package.python_versions, python_minor)

            io.write_line(
                "<warning>The currently activated Python version {} "
                "is not supported by the project ({}).\n"
                "Trying to find and use a compatible version.</warning> ".
                format(python_patch, self._poetry.package.python_versions))

            for python_to_try in reversed(
                    sorted(
                        self._poetry.package.AVAILABLE_PYTHONS,
                        key=lambda v: (v.startswith("3"), -len(v), v),
                    )):
                if len(python_to_try) == 1:
                    if not parse_constraint("^{}.0".format(
                            python_to_try)).allows_any(supported_python):
                        continue
                elif not supported_python.allows_all(
                        parse_constraint(python_to_try + ".*")):
                    continue

                python = "python" + python_to_try

                if io.is_debug():
                    io.write_line("<debug>Trying {}</debug>".format(python))

                try:
                    python_patch = decode(
                        subprocess.check_output(
                            " ".join([
                                python,
                                "-c",
                                "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"",
                            ]),
                            stderr=subprocess.STDOUT,
                            shell=True,
                        ).strip())
                except CalledProcessError:
                    continue

                if not python_patch:
                    continue

                if supported_python.allows(Version.parse(python_patch)):
                    io.write_line("Using <c1>{}</c1> ({})".format(
                        python, python_patch))
                    executable = python
                    python_minor = ".".join(python_patch.split(".")[:2])
                    break

            if not executable:
                raise NoCompatiblePythonVersionFound(
                    self._poetry.package.python_versions)

        if root_venv:
            venv = venv_path
        else:
            name = self.generate_env_name(name, str(cwd))
            name = "{}-py{}".format(name, python_minor.strip())
            venv = venv_path / name

        if not venv.exists():
            if create_venv is False:
                io.write_line("<fg=black;bg=yellow>"
                              "Skipping virtualenv creation, "
                              "as specified in config file."
                              "</>")

                return SystemEnv(Path(sys.prefix))

            io.write_line("Creating virtualenv <c1>{}</> in {}".format(
                name, str(venv_path)))

            self.build_venv(str(venv), executable=executable)
        else:
            if force:
                io.write_line("Recreating virtualenv <c1>{}</> in {}".format(
                    name, str(venv)))
                self.remove_venv(str(venv))
                self.build_venv(str(venv), executable=executable)
            elif io.is_very_verbose():
                io.write_line(
                    "Virtualenv <c1>{}</> already exists.".format(name))

        # venv detection:
        # stdlib venv may symlink sys.executable, so we can't use realpath.
        # but others can symlink *to* the venv Python,
        # so we can't just use sys.executable.
        # So we just check every item in the symlink tree (generally <= 3)
        p = os.path.normcase(sys.executable)
        paths = [p]
        while os.path.islink(p):
            p = os.path.normcase(
                os.path.join(os.path.dirname(p), os.readlink(p)))
            paths.append(p)

        p_venv = os.path.normcase(str(venv))
        if any(p.startswith(p_venv) for p in paths):
            # Running properly in the virtualenv, don't need to do anything
            return SystemEnv(Path(sys.prefix), self.get_base_prefix())

        return VirtualEnv(venv)
Beispiel #6
0
    def remove(self, python):  # type: (str) -> Env
        venv_path = self._poetry.config.get("virtualenvs.path")
        if venv_path is None:
            venv_path = Path(CACHE_DIR) / "virtualenvs"
        else:
            venv_path = Path(venv_path)

        cwd = self._poetry.file.parent
        envs_file = TomlFile(venv_path / self.ENVS_FILE)
        base_env_name = self.generate_env_name(self._poetry.package.name,
                                               str(cwd))

        if python.startswith(base_env_name):
            venvs = self.list()
            for venv in venvs:
                if venv.path.name == python:
                    # Exact virtualenv name
                    if not envs_file.exists():
                        self.remove_venv(str(venv.path))

                        return venv

                    venv_minor = ".".join(
                        str(v) for v in venv.version_info[:2])
                    base_env_name = self.generate_env_name(cwd.name, str(cwd))
                    envs = envs_file.read()

                    current_env = envs.get(base_env_name)
                    if not current_env:
                        self.remove_venv(str(venv.path))

                        return venv

                    if current_env["minor"] == venv_minor:
                        del envs[base_env_name]
                        envs_file.write(envs)

                    self.remove_venv(str(venv.path))

                    return venv

            raise ValueError(
                '<warning>Environment "{}" does not exist.</warning>'.format(
                    python))

        try:
            python_version = Version.parse(python)
            python = "python{}".format(python_version.major)
            if python_version.precision > 1:
                python += ".{}".format(python_version.minor)
        except ValueError:
            # Executable in PATH or full executable path
            pass

        try:
            python_version = decode(
                subprocess.check_output(
                    " ".join([
                        python,
                        "-c",
                        "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"",
                    ]),
                    shell=True,
                ))
        except CalledProcessError as e:
            raise EnvCommandError(e)

        python_version = Version.parse(python_version.strip())
        minor = "{}.{}".format(python_version.major, python_version.minor)

        name = "{}-py{}".format(base_env_name, minor)
        venv = venv_path / name

        if not venv.exists():
            raise ValueError(
                '<warning>Environment "{}" does not exist.</warning>'.format(
                    name))

        if envs_file.exists():
            envs = envs_file.read()
            current_env = envs.get(base_env_name)
            if current_env is not None:
                current_minor = current_env["minor"]

                if current_minor == minor:
                    del envs[base_env_name]
                    envs_file.write(envs)

        self.remove_venv(str(venv))

        return VirtualEnv(venv)
Beispiel #7
0
    def activate(self, python, io):  # type: (str, IO) -> Env
        venv_path = self._poetry.config.get("virtualenvs.path")
        if venv_path is None:
            venv_path = Path(CACHE_DIR) / "virtualenvs"
        else:
            venv_path = Path(venv_path)

        cwd = self._poetry.file.parent

        envs_file = TomlFile(venv_path / self.ENVS_FILE)

        try:
            python_version = Version.parse(python)
            python = "python{}".format(python_version.major)
            if python_version.precision > 1:
                python += ".{}".format(python_version.minor)
        except ValueError:
            # Executable in PATH or full executable path
            pass

        try:
            python_version = decode(
                subprocess.check_output(
                    " ".join([
                        python,
                        "-c",
                        "\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"",
                    ]),
                    shell=True,
                ))
        except CalledProcessError as e:
            raise EnvCommandError(e)

        python_version = Version.parse(python_version.strip())
        minor = "{}.{}".format(python_version.major, python_version.minor)
        patch = python_version.text

        create = False
        envs = tomlkit.document()
        base_env_name = self.generate_env_name(self._poetry.package.name,
                                               str(cwd))
        if envs_file.exists():
            envs = envs_file.read()
            current_env = envs.get(base_env_name)
            if current_env is not None:
                current_minor = current_env["minor"]
                current_patch = current_env["patch"]

                if current_minor == minor and current_patch != patch:
                    # We need to recreate
                    create = True

        name = "{}-py{}".format(base_env_name, minor)
        venv = venv_path / name

        # Create if needed
        if not venv.exists() or venv.exists() and create:
            in_venv = os.environ.get("VIRTUAL_ENV") is not None
            if in_venv or not venv.exists():
                create = True

            if venv.exists():
                # We need to check if the patch version is correct
                _venv = VirtualEnv(venv)
                current_patch = ".".join(
                    str(v) for v in _venv.version_info[:3])

                if patch != current_patch:
                    create = True

            self.create_venv(io, executable=python, force=create)

        # Activate
        envs[base_env_name] = {"minor": minor, "patch": patch}
        envs_file.write(envs)

        return self.get(reload=True)
def test_self_update_should_install_all_necessary_elements(
        app, http, mocker, environ, tmp_dir):
    os.environ["POETRY_HOME"] = tmp_dir

    command = app.find("self update")

    version = Version.parse(__version__).next_minor.text
    mocker.patch(
        "poetry.repositories.pypi_repository.PyPiRepository.find_packages",
        return_value=[Package("poetry", version)],
    )
    mocker.patch.object(command,
                        "_check_recommended_installation",
                        return_value=None)
    mocker.patch.object(command,
                        "_get_release_name",
                        return_value="poetry-{}-darwin".format(version))
    mocker.patch("subprocess.check_output", return_value=b"Python 3.8.2")

    http.register_uri(
        "GET",
        command.BASE_URL +
        "/{}/poetry-{}-darwin.sha256sum".format(version, version),
        body=FIXTURES.joinpath("poetry-1.0.5-darwin.sha256sum").read_bytes(),
    )
    http.register_uri(
        "GET",
        command.BASE_URL +
        "/{}/poetry-{}-darwin.tar.gz".format(version, version),
        body=FIXTURES.joinpath("poetry-1.0.5-darwin.tar.gz").read_bytes(),
    )

    tester = CommandTester(command)
    tester.execute()

    bin_ = Path(tmp_dir).joinpath("bin")
    lib = Path(tmp_dir).joinpath("lib")
    assert bin_.exists()

    script = bin_.joinpath("poetry")
    assert script.exists()

    expected_script = """\
# -*- coding: utf-8 -*-
import glob
import sys
import os

lib = os.path.normpath(os.path.join(os.path.realpath(__file__), "../..", "lib"))
vendors = os.path.join(lib, "poetry", "_vendor")
current_vendors = os.path.join(
    vendors, "py{}".format(".".join(str(v) for v in sys.version_info[:2]))
)
sys.path.insert(0, lib)
sys.path.insert(0, current_vendors)

if __name__ == "__main__":
    from poetry.console import main
    main()
"""
    if not WINDOWS:
        expected_script = "#!/usr/bin/env python\n" + expected_script

    assert expected_script == script.read_text()

    if WINDOWS:
        bat = bin_.joinpath("poetry.bat")
        expected_bat = '@echo off\r\npython "{}" %*\r\n'.format(
            str(script).replace(os.environ.get("USERPROFILE", ""),
                                "%USERPROFILE%"))
        assert bat.exists()
        with bat.open(newline="") as f:
            assert expected_bat == f.read()

    assert lib.exists()
    assert lib.joinpath("poetry").exists()