Ejemplo n.º 1
0
def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_only_extras(
    solver, repo, package
):
    dep1 = dependency_from_pep_508('B (>=1.0); extra == "foo"')
    dep1.activate()
    dep2 = dependency_from_pep_508('B (>=2.0); extra == "bar"')
    dep2.activate()

    package.add_dependency("A", {"version": "^1.0", "extras": ["foo", "bar"]})

    package_a = get_package("A", "1.0.0")
    package_a.extras = {"foo": [dep1], "bar": [dep2]}
    package_a.requires.append(dep1)
    package_a.requires.append(dep2)

    package_b2 = get_package("B", "2.0.0")
    package_b1 = get_package("B", "1.0.0")

    repo.add_package(package_a)
    repo.add_package(package_b1)
    repo.add_package(package_b2)

    ops = solver.solve()

    check_solver_result(
        ops,
        [
            {"job": "install", "package": package_b2},
            {"job": "install", "package": package_a},
        ],
    )

    assert str(ops[0].package.marker) == ""
    assert str(ops[1].package.marker) == ""
Ejemplo n.º 2
0
    def package(self, name, version, extras=None) -> 'poetry.packages.Package':
        """
        Retrieve the release information.

        This is a heavy task which takes time.
        We have to download a package to get the dependencies.
        We also need to download every file matching this release
        to get the various hashes.
        
        Note that, this will be cached so the subsequent operations
        should be much faster.
        """
        try:
            index = self._packages.index(
                poetry.packages.Package(name, version, version))

            return self._packages[index]
        except ValueError:
            if extras is None:
                extras = []

            release_info = self.get_release_info(name, version)
            package = poetry.packages.Package(name, version, version)
            for req in release_info['requires_dist']:
                try:
                    dependency = dependency_from_pep_508(req)
                except InvalidMarker:
                    # Invalid marker
                    # We strip the markers hoping for the best
                    req = req.split(';')[0]

                    dependency = dependency_from_pep_508(req)

                if dependency.extras:
                    for extra in dependency.extras:
                        if extra not in package.extras:
                            package.extras[extra] = []

                        package.extras[extra].append(dependency)

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

            # Adding description
            package.description = release_info.get('summary', '')

            # Adding hashes information
            package.hashes = release_info['digests']

            # Activate extra dependencies
            for extra in extras:
                if extra in package.extras:
                    for dep in package.extras[extra]:
                        dep.activate()

                    package.requires += package.extras[extra]

            self._packages.append(package)

            return package
Ejemplo n.º 3
0
    def package(self,
                name: str,
                version: str,
                extras: Union[list, None] = None) -> Package:
        try:
            index = self._packages.index(Package(name, version, version))

            return self._packages[index]
        except ValueError:
            if extras is None:
                extras = []

            release_info = self.get_release_info(name, version)
            package = Package(name, version, version)
            requires_dist = release_info['requires_dist'] or []
            for req in requires_dist:
                try:
                    dependency = dependency_from_pep_508(req)
                except InvalidMarker:
                    # Invalid marker
                    # We strip the markers hoping for the best
                    req = req.split(';')[0]

                    dependency = dependency_from_pep_508(req)

                if dependency.extras:
                    for extra in dependency.extras:
                        if extra not in package.extras:
                            package.extras[extra] = []

                        package.extras[extra].append(dependency)

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

            # Adding description
            package.description = release_info.get('summary', '')

            # Adding hashes information
            package.hashes = release_info['digests']

            # Activate extra dependencies
            for extra in extras:
                if extra in package.extras:
                    for dep in package.extras[extra]:
                        dep.activate()

                    package.requires += package.extras[extra]

            self._packages.append(package)

            return package
Ejemplo n.º 4
0
def test_dependency_python_version_in():
    name = ('requests (==2.18.0); ' 'python_version in \'3.3 3.4 3.5\'')
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.python_versions == '3.3.* || 3.4.* || 3.5.*'
Ejemplo n.º 5
0
def test_dependency_from_pep_508_with_extras():
    name = 'requests==2.18.0; extra == "foo" or extra == "bar"'
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.extras == ['foo', 'bar']
Ejemplo n.º 6
0
def test_dependency_platform_in():
    name = ('requests (==2.18.0); ' 'sys_platform in \'win32 darwin\'')
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.platform == 'win32 || darwin'
Ejemplo n.º 7
0
def test_dependency_platform_in():
    name = "requests (==2.18.0); " "sys_platform in 'win32 darwin'"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.platform == "win32 || darwin"
Ejemplo n.º 8
0
    def _parse_deps(self, pep508_list):
        # pylint: disable=g-import-not-at-top
        # These modules are not available on import time, but available when
        # setup.py command is running.
        from poetry import packages

        return [packages.dependency_from_pep_508(v) for v in pep508_list]
Ejemplo n.º 9
0
    def get_package_from_file(cls, file_path):  # type: (Path) -> Package
        if file_path.suffix == ".whl":
            meta = pkginfo.Wheel(str(file_path))
        else:
            # Assume sdist
            meta = pkginfo.SDist(str(file_path))

        package = Package(meta.name, meta.version)
        package.source_type = "file"
        package.source_url = file_path.as_posix()

        package.description = meta.summary
        for req in meta.requires_dist:
            dep = dependency_from_pep_508(req)
            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 meta.requires_python:
            package.python_versions = meta.requires_python

        return package
Ejemplo n.º 10
0
def test_dependency_python_version_in():
    name = "requests (==2.18.0); " "python_version in '3.3 3.4 3.5'"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.python_versions == "3.3.* || 3.4.* || 3.5.*"
Ejemplo n.º 11
0
def test_dependency_from_pep_508_with_extras():
    name = 'requests==2.18.0; extra == "foo" or extra == "bar"'
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.in_extras == ["foo", "bar"]
Ejemplo n.º 12
0
def test_solver_sets_appropriate_markers_when_solving(solver, repo, package):
    dep = dependency_from_pep_508(
        'B (>=1.0); python_version >= "3.6" and sys_platform != "win32"'
    )

    package.add_dependency("A", "^1.0")

    package_a = get_package("A", "1.0.0")
    package_a.requires.append(dep)

    package_b = get_package("B", "1.0.0")

    repo.add_package(package_a)
    repo.add_package(package_b)

    ops = solver.solve()

    check_solver_result(
        ops,
        [
            {"job": "install", "package": package_b},
            {"job": "install", "package": package_a},
        ],
    )

    assert (
        str(ops[0].package.marker)
        == 'python_version >= "3.6" and sys_platform != "win32"'
    )

    assert str(ops[1].package.marker) == ""
Ejemplo n.º 13
0
def test_solver_ignores_dependencies_with_incompatible_python_full_version_marker(
    solver, repo, package
):
    package.python_versions = "^3.6"
    package.add_dependency("A", "^1.0")
    package.add_dependency("B", "^2.0")

    package_a = get_package("A", "1.0.0")
    package_a.requires.append(
        dependency_from_pep_508(
            'B (<2.0); platform_python_implementation == "PyPy" and python_full_version < "2.7.9"'
        )
    )

    package_b200 = get_package("B", "2.0.0")
    package_b100 = get_package("B", "1.0.0")

    repo.add_package(package_a)
    repo.add_package(package_b100)
    repo.add_package(package_b200)

    ops = solver.solve()

    check_solver_result(
        ops,
        [
            {"job": "install", "package": package_a},
            {"job": "install", "package": package_b200},
        ],
    )
Ejemplo n.º 14
0
    def get_package_from_file(cls, file_path):  # type: (Path) -> Package
        info = Inspector().inspect(file_path)
        if not info["name"]:
            raise RuntimeError(
                "Unable to determine the package name of {}".format(file_path))

        package = Package(info["name"], info["version"])
        package.source_type = "file"
        package.source_url = file_path.as_posix()

        package.description = info["summary"]
        for req in info["requires_dist"]:
            dep = dependency_from_pep_508(req)
            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 info["requires_python"]:
            package.python_versions = info["requires_python"]

        return package
Ejemplo n.º 15
0
def test_dependency_from_pep_508_with_url():
    name = "django-utils @ https://example.com/django-utils-1.0.0.tar.gz"

    dep = dependency_from_pep_508(name)

    assert "django-utils" == dep.name
    assert dep.is_url()
    assert "https://example.com/django-utils-1.0.0.tar.gz" == dep.url
Ejemplo n.º 16
0
def test_dependency_from_pep_508_with_single_python_version():
    name = ('requests (==2.18.0); ' 'python_version == "2.7"')
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.extras == []
    assert dep.python_versions == '~2.7'
Ejemplo n.º 17
0
def test_dependency_python_version_in_comma():
    name = "requests (==2.18.0); python_version in '3.3, 3.4, 3.5'"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.python_versions == "3.3.* || 3.4.* || 3.5.*"
    assert str(dep.marker) == 'python_version in "3.3, 3.4, 3.5"'
Ejemplo n.º 18
0
def test_dependency_from_pep_508_with_single_python_version():
    name = "requests (==2.18.0); " 'python_version == "2.7"'
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.extras == []
    assert dep.python_versions == "~2.7"
Ejemplo n.º 19
0
 def get_build_requirements(self) -> typing.List[Dependency]:
     reqs = []
     for item in self.artifact_build_requirements:
         if isinstance(item, str):
             reqs.append(poetry_pkg.dependency_from_pep_508(item))
         else:
             reqs.append(item)
     return reqs
Ejemplo n.º 20
0
def test_dependency_from_pep_508_with_wheel_url():
    name = (
        "example_wheel @ https://example.com/example_wheel-14.0.2-py2.py3-none-any.whl"
    )

    dep = dependency_from_pep_508(name)

    assert "example-wheel" == dep.name
    assert str(dep.constraint) == "14.0.2"
Ejemplo n.º 21
0
def test_dependency_from_pep_508_with_platform():
    name = "requests (==2.18.0); " 'sys_platform == "win32" or sys_platform == "darwin"'
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.extras == []
    assert dep.python_versions == "*"
    assert dep.platform == "win32 || darwin"
Ejemplo n.º 22
0
def test_dependency_from_pep_508_with_python_version():
    name = 'requests (==2.18.0); python_version == "2.7" or python_version == "2.6"'
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.extras == []
    assert dep.python_versions == "~2.7 || ~2.6"
    assert str(dep.marker) == 'python_version == "2.7" or python_version == "2.6"'
Ejemplo n.º 23
0
def test_dependency_with_extra():
    name = 'requests[security] (==2.18.0)'
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'

    assert len(dep.extras) == 1
    assert dep.extras[0] == 'security'
Ejemplo n.º 24
0
def test_dependency_with_extra():
    name = "requests[security] (==2.18.0)"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"

    assert len(dep.extras) == 1
    assert dep.extras[0] == "security"
Ejemplo n.º 25
0
def test_dependency_from_pep_508_with_platform():
    name = ('requests (==2.18.0); '
            'sys_platform == "win32" or sys_platform == "darwin"')
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.extras == []
    assert dep.python_versions == '*'
    assert dep.platform == 'win32 || darwin'
Ejemplo n.º 26
0
def test_dependency_from_pep_508_with_git_url():
    name = "django-utils @ git+ssh://[email protected]/[email protected]"

    dep = dependency_from_pep_508(name)

    assert "django-utils" == dep.name
    assert dep.is_vcs()
    assert "git" == dep.vcs
    assert "ssh://[email protected]/corp-utils.git" == dep.source
    assert "1.2" == dep.reference
Ejemplo n.º 27
0
def test_dependency_from_pep_508_complex():
    name = ('requests (==2.18.0); '
            'python_version >= "2.7" and python_version != "3.2" '
            'and (sys_platform == "win32" or sys_platform == "darwin") '
            'and extra == "foo"')
    dep = dependency_from_pep_508(name)

    assert dep.name == 'requests'
    assert str(dep.constraint) == '== 2.18.0.0'
    assert dep.extras == ['foo']
    assert dep.python_versions == '>=2.7 !=3.2.*'
    assert dep.platform == 'win32 || darwin'
Ejemplo n.º 28
0
def test_dependency_from_pep_508_with_not_in_op_marker():
    name = ("jinja2 (>=2.7,<2.8)"
            '; python_version not in "3.0,3.1,3.2" and extra == "export"')

    dep = dependency_from_pep_508(name)

    assert dep.name == "jinja2"
    assert str(dep.constraint) == ">=2.7,<2.8"
    assert dep.in_extras == ["export"]
    assert dep.python_versions == "!=3.0.*, !=3.1.*, !=3.2.*"
    assert (str(dep.marker) ==
            'python_version not in "3.0,3.1,3.2" and extra == "export"')
Ejemplo n.º 29
0
def test_dependency_from_pep_508_with_git_url_and_comment_and_extra():
    name = (
        "poetry @ git+https://github.com/python-poetry/poetry.git@b;ar;#egg=poetry"
        ' ; extra == "foo;"')

    dep = dependency_from_pep_508(name)

    assert "poetry" == dep.name
    assert dep.is_vcs()
    assert "git" == dep.vcs
    assert "https://github.com/python-poetry/poetry.git" == dep.source
    assert "b;ar;" == dep.reference
    assert dep.in_extras == ["foo;"]
Ejemplo n.º 30
0
def test_dependency_from_pep_508_with_python_version_union_of_multi():
    name = ("requests (==2.18.0); "
            '(python_version >= "2.7" and python_version < "2.8") '
            'or (python_version >= "3.4" and python_version < "3.5")')
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.extras == []
    assert dep.python_versions == ">=2.7 <2.8 || >=3.4 <3.5"
    assert str(dep.marker) == (
        'python_version >= "2.7" and python_version < "2.8" '
        'or python_version >= "3.4" and python_version < "3.5"')
Ejemplo n.º 31
0
def test_dependency_from_pep_508_complex():
    name = (
        "requests (==2.18.0); "
        'python_version >= "2.7" and python_version != "3.2" '
        'and (sys_platform == "win32" or sys_platform == "darwin") '
        'and extra == "foo"'
    )
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
    assert dep.in_extras == ["foo"]
    assert dep.python_versions == ">=2.7 !=3.2.*"
    assert dep.platform == "win32 || darwin"
Ejemplo n.º 32
0
    def search_for_file(self, dependency):  # type: (FileDependency) -> List[Package]
        package = Package(dependency.name, dependency.pretty_constraint)
        package.source_type = "file"
        package.source_url = dependency.path.as_posix()

        package.description = dependency.metadata.summary
        for req in dependency.metadata.requires_dist:
            package.requires.append(dependency_from_pep_508(req))

        if dependency.metadata.requires_python:
            package.python_versions = dependency.metadata.requires_python

        if dependency.metadata.platforms:
            package.platform = " || ".join(dependency.metadata.platforms)

        package.hashes = [dependency.hash()]

        return [package]
Ejemplo n.º 33
0
    def package(
        self,
        name,  # type: str
        version,  # type: str
        extras=None,  # type: (Union[list, None])
    ):  # type: (...) -> Union[Package, None]
        if extras is None:
            extras = []

        release_info = self.get_release_info(name, version)
        package = Package(name, version, version)
        requires_dist = release_info["requires_dist"] or []
        for req in requires_dist:
            try:
                dependency = dependency_from_pep_508(req)
            except InvalidMarker:
                # Invalid marker
                # We strip the markers hoping for the best
                req = req.split(";")[0]

                dependency = dependency_from_pep_508(req)
            except ValueError:
                # Likely unable to parse constraint so we skip it
                self._log(
                    "Invalid constraint ({}) found in {}-{} dependencies, "
                    "skipping".format(req, package.name, package.version),
                    level="debug",
                )
                continue

            if dependency.extras:
                for extra in dependency.extras:
                    if extra not in package.extras:
                        package.extras[extra] = []

                    package.extras[extra].append(dependency)

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

        # Adding description
        package.description = release_info.get("summary", "")

        if release_info["requires_python"]:
            package.python_versions = release_info["requires_python"]

        if release_info["platform"]:
            package.platform = release_info["platform"]

        # Adding hashes information
        package.hashes = release_info["digests"]

        # Activate extra dependencies
        for extra in extras:
            if extra in package.extras:
                for dep in package.extras[extra]:
                    dep.activate()

                package.requires += package.extras[extra]

        return package
Ejemplo n.º 34
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 = Venv.create(self._io)

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

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

                    # 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))

                    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)

                    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]
Ejemplo n.º 35
0
def test_dependency_from_pep_508():
    name = "requests"
    dep = dependency_from_pep_508(name)

    assert dep.name == name
    assert str(dep.constraint) == "*"
Ejemplo n.º 36
0
def test_dependency_from_pep_508_with_constraint():
    name = "requests>=2.12.0,!=2.17.*,<3.0"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == ">=2.12.0,<2.17.0 || >=2.18.0,<3.0"
Ejemplo n.º 37
0
def test_dependency_from_pep_508_with_version():
    name = "requests==2.18.0"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"
Ejemplo n.º 38
0
    def complete_package(self, package):  # type: (str, Version) -> Package
        if package.is_root():
            return package

        if package.source_type not in {"directory", "file", "git"}:
            package = self._pool.package(
                package.name, package.version.text, extras=package.requires_extras
            )

        dependencies = [
            r
            for r in package.requires
            if r.is_activated()
            and self._package.python_constraint.allows_any(r.python_constraint)
            and self._package.platform_constraint.matches(r.platform_constraint)
        ]

        # Searching for duplicate dependencies
        #
        # If the duplicate dependencies have the same constraint,
        # the requirements will be merged.
        #
        # For instance:
        #   - enum34; python_version=="2.7"
        #   - enum34; python_version=="3.3"
        #
        # will become:
        #   - enum34; python_version=="2.7" or python_version=="3.3"
        #
        # If the duplicate dependencies have different constraints
        # we have to split the dependency graph.
        #
        # An example of this is:
        #   - pypiwin32 (220); sys_platform == "win32" and python_version >= "3.6"
        #   - pypiwin32 (219); sys_platform == "win32" and python_version < "3.6"
        if not package.is_root():
            duplicates = {}
            for dep in dependencies:
                if dep.name not in duplicates:
                    duplicates[dep.name] = []

                duplicates[dep.name].append(dep)

            dependencies = []
            for dep_name, deps in duplicates.items():
                if len(deps) == 1:
                    dependencies.append(deps[0])
                    continue

                self.debug(
                    "<debug>Duplicate dependencies for {}</debug>".format(dep_name)
                )

                # Regrouping by constraint
                by_constraint = {}
                for dep in deps:
                    if dep.constraint not in by_constraint:
                        by_constraint[dep.constraint] = []

                    by_constraint[dep.constraint].append(dep)

                # We merge by constraint
                for constraint, _deps in by_constraint.items():
                    new_markers = []
                    for dep in _deps:
                        pep_508_dep = dep.to_pep_508()
                        if ";" not in pep_508_dep:
                            continue

                        markers = pep_508_dep.split(";")[1].strip()
                        if not markers:
                            # One of the constraint has no markers
                            # so this means we don't actually need to merge
                            new_markers = []
                            break

                        new_markers.append("({})".format(markers))

                    if not new_markers:
                        dependencies += _deps
                        continue

                    dep = _deps[0]
                    new_requirement = "{}; {}".format(
                        dep.to_pep_508().split(";")[0], " or ".join(new_markers)
                    )
                    new_dep = dependency_from_pep_508(new_requirement)
                    if dep.is_optional() and not dep.is_activated():
                        new_dep.deactivate()
                    else:
                        new_dep.activate()

                    by_constraint[constraint] = [new_dep]

                    continue

                if len(by_constraint) == 1:
                    self.debug(
                        "<debug>Merging requirements for {}</debug>".format(
                            str(deps[0])
                        )
                    )
                    dependencies.append(list(by_constraint.values())[0][0])
                    continue

                # We leave dependencies as-is if they have the same
                # python/platform constraints.
                # That way the resolver will pickup the conflict
                # and display a proper error.
                _deps = [value[0] for value in by_constraint.values()]
                seen = set()
                for _dep in _deps:
                    pep_508_dep = _dep.to_pep_508()
                    if ";" not in pep_508_dep:
                        _requirements = ""
                    else:
                        _requirements = pep_508_dep.split(";")[1].strip()

                    if _requirements not in seen:
                        seen.add(_requirements)

                if len(_deps) != len(seen):
                    for _dep in _deps:
                        dependencies.append(_dep)

                    continue

                # At this point, we raise an exception that will
                # tell the solver to enter compatibility mode
                # which means it will resolve for subsets
                # Python constraints
                #
                # For instance, if our root package requires Python ~2.7 || ^3.6
                # And we have one dependency that requires Python <3.6
                # and the other Python >=3.6 than the solver will solve
                # dependencies for Python >=2.7,<2.8 || >=3.4,<3.6
                # and Python >=3.6,<4.0
                python_constraints = []
                for constraint, _deps in by_constraint.items():
                    python_constraints.append(_deps[0].python_versions)

                _deps = [str(_dep[0]) for _dep in by_constraint.values()]
                self.debug(
                    "<warning>Different requirements found for {}.</warning>".format(
                        ", ".join(_deps[:-1]) + " and " + _deps[-1]
                    )
                )
                raise CompatibilityError(*python_constraints)

        package.requires = dependencies

        return package
Ejemplo n.º 39
0
    def package(
        self, name, version, extras=None
    ):  # type: (...) -> poetry.packages.Package
        """
        Retrieve the release information.

        This is a heavy task which takes time.
        We have to download a package to get the dependencies.
        We also need to download every file matching this release
        to get the various hashes.

        Note that, this will be cached so the subsequent operations
        should be much faster.
        """
        try:
            index = self._packages.index(
                poetry.packages.Package(name, version, version)
            )

            return self._packages[index]
        except ValueError:
            if extras is None:
                extras = []

            release_info = self.get_release_info(name, version)

            package = poetry.packages.Package(name, version, version)
            package.source_type = "legacy"
            package.source_url = self._url
            package.source_reference = self.name

            requires_dist = release_info["requires_dist"] or []
            for req in requires_dist:
                try:
                    dependency = dependency_from_pep_508(req)
                except InvalidMarker:
                    # Invalid marker
                    # We strip the markers hoping for the best
                    req = req.split(";")[0]

                    dependency = dependency_from_pep_508(req)

                if dependency.extras:
                    for extra in dependency.extras:
                        if extra not in package.extras:
                            package.extras[extra] = []

                        package.extras[extra].append(dependency)

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

            # Adding description
            package.description = release_info.get("summary", "")

            # Adding hashes information
            package.hashes = release_info["digests"]

            # Activate extra dependencies
            for extra in extras:
                if extra in package.extras:
                    for dep in package.extras[extra]:
                        dep.activate()

                    package.requires += package.extras[extra]

            self._packages.append(package)

            return package
Ejemplo n.º 40
0
def test_dependency_from_pep_508_with_parens():
    name = "requests (==2.18.0)"
    dep = dependency_from_pep_508(name)

    assert dep.name == "requests"
    assert str(dep.constraint) == "2.18.0"