Beispiel #1
0
    def initialize(self, i, o):
        from poetry.semver import parse_constraint
        from poetry.utils.env import Env

        super(EnvCommand, self).initialize(i, o)

        # Checking compatibility of the current environment with
        # the python dependency specified in pyproject.toml
        current_env = Env.get()
        supported_python = self.poetry.package.python_constraint
        current_python = parse_constraint(".".join(
            str(v) for v in current_env.version_info[:3]))

        if not supported_python.allows(current_python):
            raise RuntimeError(
                "The current Python version ({}) is not supported by the project ({})\n"
                "Please activate a compatible Python version.".format(
                    current_python, self.poetry.package.python_versions))

        self._env = Env.create_venv(o,
                                    self.poetry.package.name,
                                    cwd=self.poetry.file.parent)

        if self._env.is_venv() and o.is_verbose():
            o.writeln("Using virtualenv: <comment>{}</>".format(
                self._env.path))
Beispiel #2
0
def test_virtualenvs_with_spaces_in_their_path_work_as_expected(tmp_dir):
    venv_path = Path(tmp_dir) / "Virtual Env"

    Env.build_venv(str(venv_path))

    venv = VirtualEnv(venv_path)

    assert venv.run("python", "-V", shell=True).startswith("Python")
Beispiel #3
0
def tmp_venv(tmp_dir, request):
    venv_path = Path(tmp_dir) / "venv"

    Env.build_venv(str(venv_path))

    venv = VirtualEnv(venv_path)
    yield venv

    shutil.rmtree(str(venv.path))
Beispiel #4
0
def pip_install(
    path: Union[Path, str],
    environment: Env,
    editable: bool = False,
    deps: bool = False,
    upgrade: bool = False,
) -> Union[int, str]:
    path = Path(path) if isinstance(path, str) else path
    is_wheel = path.suffix == ".whl"

    # We disable version check here as we are already pinning to version available in either the
    # virtual environment or the virtualenv package embedded wheel. Version checks are a wasteful
    # network call that adds a lot of wait time when installing a lot of packages.
    args = [
        "install", "--disable-pip-version-check", "--prefix",
        str(environment.path)
    ]

    if not is_wheel:
        args.insert(1, "--use-pep517")

    if upgrade:
        args.append("--upgrade")

    if not deps:
        args.append("--no-deps")

    if editable:
        if not path.is_dir():
            raise PoetryException(
                "Cannot install non directory dependencies in editable mode")
        args.append("-e")

    args.append(str(path))

    try:
        return environment.run_pip(*args)
    except EnvCommandError as e:
        if sys.version_info < (3, 7) and not is_wheel:
            # Under certain Python3.6 installs vendored pip wheel does not contain zip-safe
            # pep517 lib. In this cases we create an isolated ephemeral virtual environment.
            with ephemeral_environment(executable=environment.python,
                                       with_pip=True,
                                       with_setuptools=True) as env:
                return environment.run(
                    *env.get_pip_command(),
                    *args,
                    env={
                        **os.environ, "PYTHONPATH": str(env.purelib)
                    },
                )
        raise PoetryException(f"Failed to install {path.as_posix()}") from e
Beispiel #5
0
def pip_install(
    path: Union[Path, str],
    environment: Env,
    editable: bool = False,
    deps: bool = False,
    upgrade: bool = False,
) -> Union[int, str]:
    path = Path(path) if isinstance(path, str) else path

    args = ["install", "--prefix", str(environment.path)]

    if upgrade:
        args.append("--upgrade")

    if not deps:
        args.append("--no-deps")

    if editable:
        if not path.is_dir():
            raise PoetryException(
                "Cannot install non directory dependencies in editable mode")
        args.append("-e")

    args.append(str(path))

    if path.is_file() and path.suffix == ".whl":
        return environment.run_pip(*args)

    with ephemeral_environment(executable=environment.python,
                               pip=True,
                               setuptools=True) as env:
        return env.run(
            "pip",
            *args,
        )
Beispiel #6
0
    def _display_complete_info(self, env: Env) -> None:
        env_python_version = ".".join(str(s) for s in env.version_info[:3])
        self.line("")
        self.line("<b>Virtualenv</b>")
        listing = [
            f"<info>Python</info>:         <comment>{env_python_version}</>",
            f"<info>Implementation</info>: <comment>{env.python_implementation}</>",
            "<info>Path</info>:          "
            f" <comment>{env.path if env.is_venv() else 'NA'}</>",
            "<info>Executable</info>:    "
            f" <comment>{env.python if env.is_venv() else 'NA'}</>",
        ]
        if env.is_venv():
            listing.append(
                "<info>Valid</info>:         "
                f" <{'comment' if env.is_sane() else 'error'}>{env.is_sane()}</>"
            )
        self.line("\n".join(listing))

        self.line("")

        system_env = env.parent_env
        python = ".".join(str(v) for v in system_env.version_info[:3])
        self.line("<b>System</b>")
        self.line("\n".join([
            f"<info>Platform</info>:   <comment>{env.platform}</>",
            f"<info>OS</info>:         <comment>{env.os}</>",
            f"<info>Python</info>:     <comment>{python}</>",
            f"<info>Path</info>:       <comment>{system_env.path}</>",
            f"<info>Executable</info>: <comment>{system_env.python}</>",
        ]))
Beispiel #7
0
    def _inspect_sdist_with_setup(self, sdist_dir):
        info = {"requires_python": None, "requires_dist": None}

        setup = sdist_dir / "setup.py"
        if not setup.exists():
            return info

        venv = Env.create_venv(NullIO())

        current_dir = os.getcwd()
        os.chdir(sdist_dir.as_posix())

        try:
            venv.run("python", "setup.py", "egg_info")

            egg_info = list(sdist_dir.glob("**/*.egg-info"))[0]

            meta = pkginfo.UnpackedSDist(str(egg_info))
            if meta.requires_python:
                info["requires_python"] = meta.requires_python

            if meta.requires_dist:
                info["requires_dist"] = list(meta.requires_dist)
            else:
                requires = egg_info / "requires.txt"
                if requires.exists():
                    with requires.open() as f:
                        info["requires_dist"] = parse_requires(f.read())
        except Exception:
            pass

        os.chdir(current_dir)

        return info
Beispiel #8
0
def pip_install(
    path: Union[Path, str],
    environment: Env,
    editable: bool = False,
    deps: bool = False,
    upgrade: bool = False,
) -> Union[int, str]:
    path = Path(path) if isinstance(path, str) else path
    is_wheel = path.suffix == ".whl"

    args = ["install", "--prefix", str(environment.path)]

    if not is_wheel:
        args.insert(1, "--use-pep517")

    if upgrade:
        args.append("--upgrade")

    if not deps:
        args.append("--no-deps")

    if editable:
        if not path.is_dir():
            raise PoetryException(
                "Cannot install non directory dependencies in editable mode")
        args.append("-e")

    args.append(str(path))

    try:
        return environment.run_pip(*args)
    except EnvCommandError as e:
        if sys.version_info < (3, 7) and not is_wheel:
            # Under certain Python3.6 installs vendored pip wheel does not contain zip-safe
            # pep517 lib. In this cases we create an isolated ephemeral virtual environment.
            with ephemeral_environment(executable=environment.python,
                                       pip=True,
                                       setuptools=True) as env:
                return environment.run(
                    env._bin("pip"),
                    *args,
                    env={
                        **os.environ, "PYTHONPATH": str(env.purelib)
                    },
                )
        raise PoetryException(f"Failed to install {path.as_posix()}") from e
Beispiel #9
0
def test_env_has_symlinks_on_nix(tmp_dir):
    venv_path = Path(tmp_dir) / "Virtual Env"

    Env.build_venv(str(venv_path))

    venv = VirtualEnv(venv_path)

    venv_available = False
    try:
        from venv import EnvBuilder

        venv_available = True
    except ImportError:
        pass

    if os.name != "nt" and venv_available:
        assert os.path.islink(venv.python)
Beispiel #10
0
def test_env_get_in_project_venv(tmp_dir, environ):
    if "VIRTUAL_ENV" in environ:
        del environ["VIRTUAL_ENV"]

    (Path(tmp_dir) / ".venv").mkdir()

    venv = Env.get(cwd=Path(tmp_dir))

    assert venv.path == Path(tmp_dir) / ".venv"
Beispiel #11
0
    def initialize(self, i, o):
        from poetry.utils.env import Env

        super(EnvCommand, self).initialize(i, o)

        self._env = Env.create_venv(o,
                                    self.poetry.package.name,
                                    cwd=self.poetry.file.parent)

        if self._env.is_venv() and o.is_verbose():
            o.writeln("Using virtualenv: <comment>{}</>".format(
                self._env.path))
Beispiel #12
0
    def handle(self):
        from poetry.layouts import layout
        from poetry.utils._compat import Path
        from poetry.utils.env import Env
        from poetry.vcs.git import GitConfig

        if self.option("src"):
            layout_ = layout("src")
        else:
            layout_ = layout("standard")

        path = Path.cwd() / Path(self.argument("path"))
        name = self.option("name")
        if not name:
            name = path.name

        if path.exists():
            if list(path.glob("*")):
                # Directory is not empty. Aborting.
                raise RuntimeError("Destination <fg=yellow>{}</> "
                                   "exists and is not empty".format(path))

        readme_format = "rst"

        config = GitConfig()
        author = None
        if config.get("user.name"):
            author = config["user.name"]
            author_email = config.get("user.email")
            if author_email:
                author += " <{}>".format(author_email)

        current_env = Env.get()
        default_python = "^{}".format(".".join(
            str(v) for v in current_env.version_info[:2]))
        layout_ = layout_(
            name,
            "0.1.0",
            author=author,
            readme_format=readme_format,
            python=default_python,
        )
        layout_.create(path)

        self.line("Created package <info>{}</> in <fg=blue>{}</>".format(
            name, path.relative_to(Path.cwd())))
Beispiel #13
0
def pip_install(
    path: Path | Link,
    environment: Env,
    editable: bool = False,
    deps: bool = False,
    upgrade: bool = False,
) -> int | str:
    path = url_to_path(path.url) if isinstance(path, Link) else path
    is_wheel = path.suffix == ".whl"

    # We disable version check here as we are already pinning to version available in
    # either the virtual environment or the virtualenv package embedded wheel. Version
    # checks are a wasteful network call that adds a lot of wait time when installing a
    # lot of packages.
    args = [
        "install", "--disable-pip-version-check", "--prefix",
        str(environment.path)
    ]

    if not is_wheel:
        args.insert(1, "--use-pep517")

    if upgrade:
        args.append("--upgrade")

    if not deps:
        args.append("--no-deps")

    if editable:
        if not path.is_dir():
            raise PoetryException(
                "Cannot install non directory dependencies in editable mode")
        args.append("-e")

    args.append(str(path))

    try:
        return environment.run_pip(*args)
    except EnvCommandError as e:
        raise PoetryException(f"Failed to install {path.as_posix()}") from e
Beispiel #14
0
    def handle(self):
        from poetry.layouts import layout
        from poetry.utils._compat import Path
        from poetry.utils.env import Env
        from poetry.vcs.git import GitConfig

        if (Path.cwd() / "pyproject.toml").exists():
            self.error("A pyproject.toml file already exists.")
            return 1

        vcs_config = GitConfig()

        self.line([
            "",
            "This command will guide you through creating your <info>pyproject.toml</> config.",
            "",
        ])

        name = self.option("name")
        if not name:
            name = Path.cwd().name.lower()

            question = self.create_question(
                "Package name [<comment>{}</comment>]: ".format(name),
                default=name)
            name = self.ask(question)

        version = "0.1.0"
        question = self.create_question(
            "Version [<comment>{}</comment>]: ".format(version),
            default=version)
        version = self.ask(question)

        description = self.option("description") or ""
        question = self.create_question(
            "Description [<comment>{}</comment>]: ".format(description),
            default=description,
        )
        description = self.ask(question)

        author = self.option("author")
        if not author and vcs_config and vcs_config.get("user.name"):
            author = vcs_config["user.name"]
            author_email = vcs_config.get("user.email")
            if author_email:
                author += " <{}>".format(author_email)

        question = self.create_question(
            "Author [<comment>{}</comment>, n to skip]: ".format(author),
            default=author)
        question.validator = lambda v: self._validate_author(v, author)
        author = self.ask(question)

        if not author:
            authors = []
        else:
            authors = [author]

        license = self.option("license") or ""

        question = self.create_question(
            "License [<comment>{}</comment>]: ".format(license),
            default=license)
        question.validator = self._validate_license
        license = self.ask(question)

        current_env = Env.get()
        default_python = "^{}".format(".".join(
            str(v) for v in current_env.version_info[:2]))
        question = self.create_question(
            "Compatible Python versions [<comment>{}</comment>]: ".format(
                default_python),
            default=default_python,
        )
        python = self.ask(question)

        self.line("")

        requirements = {}

        question = ("Would you like to define your dependencies"
                    " (require) interactively?")
        if self.confirm(question, True):
            requirements = self._format_requirements(
                self._determine_requirements(self.option("dependency")))

        dev_requirements = {}

        question = ("Would you like to define your dev dependencies"
                    " (require-dev) interactively")
        if self.confirm(question, True):
            dev_requirements = self._format_requirements(
                self._determine_requirements(self.option("dev-dependency")))

        layout_ = layout("standard")(
            name,
            version,
            description=description,
            author=authors[0] if authors else None,
            license=license,
            python=python,
            dependencies=requirements,
            dev_dependencies=dev_requirements,
        )

        content = layout_.generate_poetry_content()
        if self.input.is_interactive():
            self.line("<info>Generated file</info>")
            self.line(["", content, ""])

        if not self.confirm("Do you confirm generation?", True):
            self.line("<error>Command aborted</error>")

            return 1

        with (Path.cwd() / "pyproject.toml").open("w") as f:
            f.write(content)
Beispiel #15
0
    def handle(self):
        from poetry.packages import ProjectPackage
        from poetry.puzzle import Solver
        from poetry.repositories.repository import Repository
        from poetry.semver import parse_constraint
        from poetry.utils.env import Env

        packages = self.argument("package")

        if not packages:
            package = self.poetry.package
        else:
            package = ProjectPackage(self.poetry.package.name,
                                     self.poetry.package.version)
            requirements = self._format_requirements(packages)

            for name, constraint in requirements.items():
                dep = package.add_dependency(name, constraint)
                extras = []
                for extra in self.option("extras"):
                    if " " in extra:
                        extras += [e.strip() for e in extra.split(" ")]
                    else:
                        extras.append(extra)

                for ex in extras:
                    dep.extras.append(ex)

        package.python_versions = self.option("python") or (
            self.poetry.package.python_versions)

        pool = self.poetry.pool

        solver = Solver(package, pool, Repository(), Repository(), self.output)

        ops = solver.solve()

        self.line("")
        self.line("Resolution results:")
        self.line("")

        if self.option("tree"):
            show_command = self.get_application().find("show")
            show_command.output = self.output
            show_command.init_styles()

            packages = [op.package for op in ops]
            repo = Repository(packages)

            requires = package.requires + package.dev_requires
            for pkg in repo.packages:
                for require in requires:
                    if pkg.name == require.name:
                        show_command.display_package_tree(pkg, repo)
                        break

            return 0

        env = Env.get()
        current_python_version = parse_constraint(".".join(
            str(v) for v in env.version_info))
        for op in ops:
            pkg = op.package
            if self.option("install"):
                if not pkg.python_constraint.allows(
                        current_python_version) or not env.is_valid_for_marker(
                            pkg.marker):
                    continue

            self.line("  - <info>{}</info> (<comment>{}</comment>)".format(
                pkg.name, pkg.version))
            if not pkg.python_constraint.is_any():
                self.line("    - python: {}".format(pkg.python_versions))

            if not pkg.marker.is_any():
                self.line("    - marker: {}".format(pkg.marker))
Beispiel #16
0
    def search_for_directory(
            self, dependency):  # type: (DirectoryDependency) -> List[Package]
        if dependency.supports_poetry():
            from poetry.poetry import Poetry

            poetry = Poetry.create(dependency.full_path)

            pkg = poetry.package
            package = Package(pkg.name, pkg.version)

            for dep in pkg.requires:
                if not dep.is_optional():
                    package.requires.append(dep)

            for extra, deps in pkg.extras.items():
                if extra not in package.extras:
                    package.extras[extra] = []

                for dep in deps:
                    package.extras[extra].append(dep)

            package.python_versions = pkg.python_versions
        else:
            # Execute egg_info
            current_dir = os.getcwd()
            os.chdir(str(dependency.full_path))

            try:
                cwd = dependency.full_path
                venv = Env.get(NullIO(), cwd=cwd)
                venv.run("python", "setup.py", "egg_info")
            except EnvCommandError:
                result = SetupReader.read_from_directory(dependency.full_path)
                if not result["name"]:
                    # The name could not be determined
                    # We use the dependency name
                    result["name"] = dependency.name

                if not result["version"]:
                    # The version could not be determined
                    # so we raise an error since it is mandatory
                    raise RuntimeError(
                        "Unable to retrieve the package version for {}".format(
                            dependency.path))

                package_name = result["name"]
                package_version = result["version"]
                python_requires = result["python_requires"]
                if python_requires is None:
                    python_requires = "*"

                package_summary = ""

                requires = ""
                for dep in result["install_requires"]:
                    requires += dep + "\n"

                if result["extras_require"]:
                    requires += "\n"

                for extra_name, deps in result["extras_require"].items():
                    requires += "[{}]\n".format(extra_name)

                    for dep in deps:
                        requires += dep + "\n"

                    requires += "\n"

                reqs = parse_requires(requires)
            else:
                os.chdir(current_dir)
                # Sometimes pathlib will fail on recursive
                # symbolic links, so we need to workaround it
                # and use the glob module instead.
                # Note that this does not happen with pathlib2
                # so it's safe to use it for Python < 3.4.
                if PY35:
                    egg_info = next(
                        Path(p) for p in glob.glob(
                            os.path.join(str(dependency.full_path), "**",
                                         "*.egg-info"),
                            recursive=True,
                        ))
                else:
                    egg_info = next(dependency.full_path.glob("**/*.egg-info"))

                meta = pkginfo.UnpackedSDist(str(egg_info))
                package_name = meta.name
                package_version = meta.version
                package_summary = meta.summary
                python_requires = meta.requires_python

                if meta.requires_dist:
                    reqs = list(meta.requires_dist)
                else:
                    reqs = []
                    requires = egg_info / "requires.txt"
                    if requires.exists():
                        with requires.open() as f:
                            reqs = parse_requires(f.read())
            finally:
                os.chdir(current_dir)

            package = Package(package_name, package_version)

            if dependency.name != package.name:
                # For now, the dependency's name must match the actual package's name
                raise RuntimeError(
                    "The dependency name for {} does not match the actual package's name: {}"
                    .format(dependency.name, package.name))

            package.description = package_summary

            for req in reqs:
                dep = dependency_from_pep_508(req)
                if dep.in_extras:
                    for extra in dep.in_extras:
                        if extra not in package.extras:
                            package.extras[extra] = []

                        package.extras[extra].append(dep)

                if not dep.is_optional():
                    package.requires.append(dep)

            if python_requires:
                package.python_versions = python_requires

        package.source_type = "directory"
        package.source_url = dependency.path.as_posix()

        for extra in dependency.extras:
            if extra in package.extras:
                for dep in package.extras[extra]:
                    dep.activate()

                package.requires += package.extras[extra]

        return [package]
Beispiel #17
0
    def __init__(
            self,
            path,  # type: Path
            category="main",  # type: str
            optional=False,  # type: bool
            base=None,  # type: Path
            develop=True,  # type: bool
    ):
        from . import dependency_from_pep_508
        from .package import Package

        self._path = path
        self._base = base
        self._full_path = path
        self._develop = develop
        self._supports_poetry = False

        if self._base and not self._path.is_absolute():
            self._full_path = self._base / self._path

        if not self._full_path.exists():
            raise ValueError("Directory {} does not exist".format(self._path))

        if self._full_path.is_file():
            raise ValueError("{} is a file, expected a directory".format(
                self._path))

        # Checking content to dertermine actions
        setup = self._full_path / "setup.py"
        pyproject = TomlFile(self._full_path / "pyproject.toml")
        if pyproject.exists():
            pyproject_content = pyproject.read()
            self._supports_poetry = ("tool" in pyproject_content
                                     and "poetry" in pyproject_content["tool"])

        if not setup.exists() and not self._supports_poetry:
            raise ValueError(
                "Directory {} does not seem to be a Python package".format(
                    self._full_path))

        if self._supports_poetry:
            from poetry.poetry import Poetry

            poetry = Poetry.create(self._full_path)

            package = poetry.package
            self._package = Package(package.pretty_name, package.version)
            self._package.requires += package.requires
            self._package.dev_requires += package.dev_requires
            self._package.extras = package.extras
            self._package.python_versions = package.python_versions
        else:
            # Execute egg_info
            current_dir = os.getcwd()
            os.chdir(str(self._full_path))

            try:
                cwd = base
                venv = Env.get(NullIO(), cwd=cwd)
                venv.run("python", "setup.py", "egg_info")
            except EnvCommandError:
                result = SetupReader.read_from_directory(self._full_path)
                if not result["name"]:
                    # The name could not be determined
                    # so we raise an error since it is mandatory
                    raise RuntimeError(
                        "Unable to retrieve the package name for {}".format(
                            path))

                if not result["version"]:
                    # The version could not be determined
                    # so we raise an error since it is mandatory
                    raise RuntimeError(
                        "Unable to retrieve the package version for {}".format(
                            path))

                package_name = result["name"]
                package_version = result["version"]
                python_requires = result["python_requires"]
                if python_requires is None:
                    python_requires = "*"

                package_summary = ""

                requires = ""
                for dep in result["install_requires"]:
                    requires += dep + "\n"

                if result["extras_require"]:
                    requires += "\n"

                for extra_name, deps in result["extras_require"].items():
                    requires += "[{}]\n".format(extra_name)

                    for dep in deps:
                        requires += dep + "\n"

                    requires += "\n"

                reqs = parse_requires(requires)
            else:
                os.chdir(current_dir)
                # Sometimes pathlib will fail on recursive
                # symbolic links, so we need to workaround it
                # and use the glob module instead.
                # Note that this does not happen with pathlib2
                # so it's safe to use it for Python < 3.4.
                if PY35:
                    egg_info = next(
                        Path(p) for p in glob.glob(
                            os.path.join(str(self._full_path), "**",
                                         "*.egg-info"),
                            recursive=True,
                        ))
                else:
                    egg_info = next(self._full_path.glob("**/*.egg-info"))

                meta = pkginfo.UnpackedSDist(str(egg_info))
                package_name = meta.name
                package_version = meta.version
                package_summary = meta.summary
                python_requires = meta.requires_python

                if meta.requires_dist:
                    reqs = list(meta.requires_dist)
                else:
                    reqs = []
                    requires = egg_info / "requires.txt"
                    if requires.exists():
                        with requires.open() as f:
                            reqs = parse_requires(f.read())
            finally:
                os.chdir(current_dir)

            package = Package(package_name, package_version)
            package.description = package_summary

            for req in reqs:
                package.requires.append(dependency_from_pep_508(req))

            if python_requires:
                package.python_versions = python_requires

            self._package = package

        self._package.source_type = "directory"
        self._package.source_url = self._path.as_posix()

        super(DirectoryDependency, self).__init__(
            self._package.name,
            self._package.version,
            category=category,
            optional=optional,
            allows_prereleases=True,
        )
Beispiel #18
0
    def load(cls, env: Env) -> "InstalledRepository":
        """
        Load installed packages.
        """
        repo = cls()
        seen = set()

        for entry in reversed(env.sys_path):
            for distribution in sorted(
                    metadata.distributions(path=[entry]),
                    key=lambda d: str(d._path),
            ):
                name = distribution.metadata["name"]
                path = Path(str(distribution._path))
                version = distribution.metadata["version"]
                package = Package(name, version, version)
                package.description = distribution.metadata.get("summary", "")

                if package.name in seen:
                    continue

                try:
                    path.relative_to(_VENDORS)
                except ValueError:
                    pass
                else:
                    continue

                seen.add(package.name)

                repo.add_package(package)

                is_standard_package = env.is_path_relative_to_lib(path)

                if is_standard_package:
                    if path.name.endswith(".dist-info"):
                        paths = cls.get_package_paths(env=env,
                                                      name=package.pretty_name)
                        if paths:
                            is_editable_package = False
                            for src in paths:
                                if cls.is_vcs_package(src, env):
                                    cls.set_package_vcs_properties(
                                        package, env)
                                    break

                                if not (is_editable_package
                                        or env.is_path_relative_to_lib(src)):
                                    is_editable_package = True
                            else:
                                # TODO: handle multiple source directories?
                                if is_editable_package:
                                    package._source_type = "directory"
                                    package._source_url = paths.pop().as_posix(
                                    )
                    continue

                if cls.is_vcs_package(path, env):
                    cls.set_package_vcs_properties(package, env)
                else:
                    # If not, it's a path dependency
                    package._source_type = "directory"
                    package._source_url = str(path.parent)

        return repo
    def __init__(
            self,
            path,  # type: Path
            category="main",  # type: str
            optional=False,  # type: bool
            base=None,  # type: Path
            develop=True,  # type: bool
    ):
        from . import dependency_from_pep_508
        from .package import Package

        self._path = path
        self._base = base
        self._full_path = path
        self._develop = develop
        self._supports_poetry = False

        if self._base and not self._path.is_absolute():
            self._full_path = self._base / self._path

        if not self._full_path.exists():
            raise ValueError("Directory {} does not exist".format(self._path))

        if self._full_path.is_file():
            raise ValueError("{} is a file, expected a directory".format(
                self._path))

        # Checking content to dertermine actions
        setup = self._full_path / "setup.py"
        pyproject = TomlFile(self._full_path / "pyproject.toml")
        if pyproject.exists():
            pyproject_content = pyproject.read()
            self._supports_poetry = ("tool" in pyproject_content
                                     and "poetry" in pyproject_content["tool"])

        if not setup.exists() and not self._supports_poetry:
            raise ValueError(
                "Directory {} does not seem to be a Python package".format(
                    self._full_path))

        if self._supports_poetry:
            from poetry.poetry import Poetry

            poetry = Poetry.create(self._full_path)

            package = poetry.package
            self._package = Package(package.pretty_name, package.version)
            self._package.requires += package.requires
            self._package.dev_requires += package.dev_requires
            self._package.extras = package.extras
            self._package.python_versions = package.python_versions
        else:
            # Execute egg_info
            current_dir = os.getcwd()
            os.chdir(str(self._full_path))

            try:
                cwd = base
                venv = Env.create_venv(NullIO(), cwd=cwd)
                venv.run("python", "setup.py", "egg_info")
            finally:
                os.chdir(current_dir)

            egg_info = list(self._full_path.glob("*.egg-info"))[0]

            meta = pkginfo.UnpackedSDist(str(egg_info))

            if meta.requires_dist:
                reqs = list(meta.requires_dist)
            else:
                reqs = []
                requires = egg_info / "requires.txt"
                if requires.exists():
                    with requires.open() as f:
                        reqs = parse_requires(f.read())

            package = Package(meta.name, meta.version)
            package.description = meta.summary

            for req in reqs:
                package.requires.append(dependency_from_pep_508(req))

            if meta.requires_python:
                package.python_versions = meta.requires_python

            if meta.platforms:
                platforms = [
                    p for p in meta.platforms if p.lower() != "unknown"
                ]
                if platforms:
                    package.platform = " || ".join(platforms)

            self._package = package

        self._package.source_type = "directory"
        self._package.source_url = self._path.as_posix()

        super(DirectoryDependency, self).__init__(
            self._package.name,
            self._package.version,
            category=category,
            optional=optional,
            allows_prereleases=True,
        )
    def create_package_from_distribution(
        cls, distribution: metadata.Distribution, env: Env
    ) -> Package:
        # We first check for a direct_url.json file to determine
        # the type of package.
        path = Path(str(distribution._path))

        if (
            path.name.endswith(".dist-info")
            and path.joinpath("direct_url.json").exists()
        ):
            return cls.create_package_from_pep610(distribution)

        is_standard_package = env.is_path_relative_to_lib(path)

        source_type = None
        source_url = None
        source_reference = None
        source_resolved_reference = None
        if is_standard_package:
            if path.name.endswith(".dist-info"):
                paths = cls.get_package_paths(
                    env=env, name=distribution.metadata["name"]
                )
                if paths:
                    is_editable_package = False
                    for src in paths:
                        if cls.is_vcs_package(src, env):
                            (
                                source_type,
                                source_url,
                                source_reference,
                            ) = cls.get_package_vcs_properties_from_path(src)
                            break

                        if not (
                            is_editable_package or env.is_path_relative_to_lib(src)
                        ):
                            is_editable_package = True
                    else:
                        # TODO: handle multiple source directories?
                        if is_editable_package:
                            source_type = "directory"
                            source_url = paths.pop().as_posix()
        elif cls.is_vcs_package(path, env):
            (
                source_type,
                source_url,
                source_reference,
            ) = cls.get_package_vcs_properties_from_path(
                env.path / "src" / canonicalize_name(distribution.metadata["name"])
            )
        else:
            # If not, it's a path dependency
            source_type = "directory"
            source_url = str(path.parent)

        package = Package(
            distribution.metadata["name"],
            distribution.metadata["version"],
            source_type=source_type,
            source_url=source_url,
            source_reference=source_reference,
            source_resolved_reference=source_resolved_reference,
        )
        package.description = distribution.metadata.get("summary", "")

        return package
Beispiel #21
0
    def search_for_vcs(self,
                       dependency):  # type: (VCSDependency) -> List[Package]
        """
        Search for the specifications that match the given VCS dependency.

        Basically, we clone the repository in a temporary directory
        and get the information we need by checking out the specified reference.
        """
        if dependency.vcs != "git":
            raise ValueError("Unsupported VCS dependency {}".format(
                dependency.vcs))

        tmp_dir = Path(
            mkdtemp(prefix="pypoetry-git-{}".format(dependency.name)))

        try:
            git = Git()
            git.clone(dependency.source, tmp_dir)
            git.checkout(dependency.reference, tmp_dir)
            revision = git.rev_parse(dependency.reference, tmp_dir).strip()

            if dependency.tag or dependency.rev:
                revision = dependency.reference

            pyproject = TomlFile(tmp_dir / "pyproject.toml")
            pyproject_content = None
            has_poetry = False
            if pyproject.exists():
                pyproject_content = pyproject.read()
                has_poetry = ("tool" in pyproject_content
                              and "poetry" in pyproject_content["tool"])

            if pyproject_content and has_poetry:
                # If a pyproject.toml file exists
                # We use it to get the information we need
                info = pyproject_content["tool"]["poetry"]

                name = info["name"]
                version = info["version"]
                package = Package(name, version, version)
                package.source_type = dependency.vcs
                package.source_url = dependency.source
                package.source_reference = dependency.reference
                for req_name, req_constraint in info["dependencies"].items():
                    if req_name == "python":
                        package.python_versions = req_constraint
                        continue

                    package.add_dependency(req_name, req_constraint)
            else:
                # We need to use setup.py here
                # to figure the information we need
                # We need to place ourselves in the proper
                # folder for it to work
                venv = Env.get(self._io)

                current_dir = os.getcwd()
                os.chdir(tmp_dir.as_posix())

                try:
                    try:
                        venv.run("python", "setup.py", "egg_info")
                    except EnvCommandError:
                        # Most likely an error with the egg_info command
                        self.debug(
                            "<warning>Error executing the egg_info command. Reading setup files.</warning>"
                        )
                        result = SetupReader.read_from_directory(tmp_dir)
                        if not result["name"]:
                            result["name"] = dependency.name

                        if not result["version"]:
                            # The version could not be determined
                            # so we raise an error since it is mandatory
                            raise RuntimeError(
                                "Unable to retrieve the version for {}".format(
                                    dependency.name))

                        package_name = result["name"]
                        package_version = result["version"]
                        python_requires = result["python_requires"]

                        requires = ""
                        for dep in result["install_requires"]:
                            requires += dep + "\n"

                        if result["extras_require"]:
                            requires += "\n"

                        for extra_name, deps in result["extras_require"].items(
                        ):
                            requires += "[{}]\n".format(extra_name)

                            for dep in deps:
                                requires += dep + "\n"

                            requires += "\n"

                        reqs = parse_requires(requires)
                    else:
                        # Sometimes pathlib will fail on recursive
                        # symbolic links, so we need to workaround it
                        # and use the glob module instead.
                        # Note that this does not happen with pathlib2
                        # so it's safe to use it for Python < 3.4.
                        if PY35:
                            egg_info = next(
                                Path(p) for p in glob.glob(
                                    os.path.join(str(tmp_dir), "**",
                                                 "*.egg-info"),
                                    recursive=True,
                                ))
                        else:
                            egg_info = next(tmp_dir.glob("**/*.egg-info"))

                        meta = pkginfo.UnpackedSDist(str(egg_info))

                        package_name = meta.name
                        package_version = meta.version
                        python_requires = meta.requires_python

                        if meta.requires_dist:
                            reqs = list(meta.requires_dist)
                        else:
                            reqs = []
                            requires = egg_info / "requires.txt"
                            if requires.exists():
                                with requires.open() as f:
                                    reqs = parse_requires(f.read())

                    package = Package(package_name, package_version)
                    if python_requires:
                        package.python_versions = python_requires

                    for req in reqs:
                        dep = dependency_from_pep_508(req)
                        if dep.in_extras:
                            for extra in dep.in_extras:
                                if extra not in package.extras:
                                    package.extras[extra] = []

                                package.extras[extra].append(dep)

                        package.requires.append(dep)
                except Exception:
                    raise
                finally:
                    os.chdir(current_dir)

            package.source_type = "git"
            package.source_url = dependency.source
            package.source_reference = revision
        except Exception:
            raise
        finally:
            shutil.rmtree(tmp_dir.as_posix())

        if dependency.name != package.name:
            # For now, the dependency's name must match the actual package's name
            raise RuntimeError(
                "The dependency name for {} does not match the actual package's name: {}"
                .format(dependency.name, package.name))

        if dependency.extras:
            for extra in dependency.extras:
                if extra in package.extras:
                    for dep in package.extras[extra]:
                        dep.activate()

        return [package]