test_resolve.py 8.6 KB
Newer Older
F
frostming 已提交
1
import pytest
F
frostming 已提交
2
from resolvelib.resolvers import ResolutionImpossible, Resolver
F
Frost Ming 已提交
3

F
Frost Ming 已提交
4
from pdm.iostream import stream
F
frostming 已提交
5 6
from pdm.models.requirements import parse_requirement
from pdm.models.specifiers import PySpecSet
F
frostming 已提交
7 8 9
from pdm.resolver import resolve
from pdm.resolver.providers import BaseProvider, EagerUpdateProvider, ReusePinProvider
from pdm.resolver.reporters import SpinnerReporter
F
frostming 已提交
10 11 12 13
from tests import FIXTURES


def resolve_requirements(
F
linting  
Frost Ming 已提交
14 15 16 17
    repository,
    lines,
    requires_python="",
    allow_prereleases=None,
F
Frost Ming 已提交
18
    strategy="all",
F
linting  
Frost Ming 已提交
19 20
    preferred_pins=None,
    tracked_names=None,
F
frostming 已提交
21
):
F
Frost Ming 已提交
22 23 24 25 26 27
    requirements = []
    for line in lines:
        if line.startswith("-e "):
            requirements.append(parse_requirement(line[3:], True))
        else:
            requirements.append(parse_requirement(line))
F
frostming 已提交
28 29 30 31 32 33 34 35
    requires_python = PySpecSet(requires_python)
    if not preferred_pins:
        provider = BaseProvider(repository, requires_python, allow_prereleases)
    else:
        provider_class = (
            ReusePinProvider if strategy == "reuse" else EagerUpdateProvider
        )
        provider = provider_class(
F
linting  
Frost Ming 已提交
36 37 38 39 40
            preferred_pins,
            tracked_names or (),
            repository,
            requires_python,
            allow_prereleases,
F
frostming 已提交
41
        )
F
Frost Ming 已提交
42

F
frostming 已提交
43
    with stream.open_spinner("Resolving dependencies") as spin, stream.logging("lock"):
F
Frost Ming 已提交
44
        reporter = SpinnerReporter(spin, requirements)
F
Frost Ming 已提交
45
        resolver = Resolver(provider, reporter)
F
Frost Ming 已提交
46
        mapping, *_ = resolve(resolver, requirements, requires_python)
F
frostming 已提交
47
        return mapping
F
frostming 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68


def test_resolve_named_requirement(project, repository):
    result = resolve_requirements(repository, ["requests"])

    assert result["requests"].version == "2.19.1"
    assert result["urllib3"].version == "1.22"
    assert result["chardet"].version == "3.0.4"
    assert result["certifi"].version == "2018.11.17"
    assert result["idna"].version == "2.7"


def test_resolve_requires_python(project, repository):
    result = resolve_requirements(repository, ["django"])
    assert result["django"].version == "1.11.8"
    assert "sqlparse" not in result

    result = resolve_requirements(repository, ["django"], ">=3.6")
    assert result["django"].version == "2.2.9"
    assert "sqlparse" in result

69 70 71 72
    result = resolve_requirements(repository, ["django; python_version>='3.7'"])
    assert result["django"].version == "2.2.9"
    assert "sqlparse" in result

F
frostming 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

def test_resolve_allow_prereleases(project, repository):
    repository.add_candidate("foo", "1.0.0")
    repository.add_candidate("foo", "1.1.0-alpha")
    repository.add_candidate("bar", "1.0.0-beta")

    result = resolve_requirements(repository, ["foo"])
    assert result["foo"].version == "1.0.0"

    result = resolve_requirements(repository, ["foo"], allow_prereleases=True)
    assert result["foo"].version == "1.1.0-alpha"

    result = resolve_requirements(repository, ["foo==1.1.0-alpha"])
    assert result["foo"].version == "1.1.0-alpha"

    result = resolve_requirements(repository, ["bar"])
    assert result["bar"].version == "1.0.0-beta"

    with pytest.raises(Exception):
        resolve_requirements(repository, ["bar"], allow_prereleases=False)


def test_resolve_with_extras(project, repository):

F
Frost Ming 已提交
97
    result = resolve_requirements(repository, ["requests[socks]"])
F
frostming 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    assert result["pysocks"].version == "1.5.6"


@pytest.mark.parametrize(
    "requirement_line",
    [
        f"{(FIXTURES / 'artifacts/demo-0.0.1.tar.gz').as_posix()}",
        f"{(FIXTURES / 'artifacts/demo-0.0.1-py2.py3-none-any.whl').as_posix()}",
    ],
)
def test_resolve_local_artifacts(project, repository, requirement_line):
    result = resolve_requirements(repository, [requirement_line])
    assert result["idna"].version == "2.7"


@pytest.mark.parametrize(
    "line",
    [
F
Frost Ming 已提交
116
        (FIXTURES / "projects/demo").as_posix(),
F
frostming 已提交
117 118 119 120 121 122
        "git+https://github.com/test-root/demo.git#egg=demo",
    ],
)
def test_resolve_vcs_and_local_requirements(
    project, repository, line, is_editable, vcs
):
F
Frost Ming 已提交
123 124
    editable = "-e " if is_editable else ""
    result = resolve_requirements(repository, [editable + line])
F
frostming 已提交
125 126 127
    assert result["idna"].version == "2.7"


F
Frost Ming 已提交
128 129 130 131 132 133 134 135 136 137
def test_resolve_local_and_named_requirement(project, repository, vcs):
    requirements = ["demo", "git+https://github.com/test-root/demo.git#egg=demo"]
    result = resolve_requirements(repository, requirements, ">=3.6")
    assert result["demo"].req.repo == "https://github.com/test-root/demo.git"

    requirements = ["git+https://github.com/test-root/demo.git#egg=demo", "demo"]
    result = resolve_requirements(repository, requirements, ">=3.6")
    assert result["demo"].req.repo == "https://github.com/test-root/demo.git"


F
frostming 已提交
138
def test_resolving_auto_avoid_conflicts(project, repository):
F
frostming 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    repository.add_candidate("foo", "0.1.0")
    repository.add_candidate("foo", "0.2.0")
    repository.add_dependencies("foo", "0.1.0", ["hoho<2.0"])
    repository.add_dependencies("foo", "0.2.0", ["hoho>=2.0"])
    repository.add_candidate("bar", "0.1.0")
    repository.add_dependencies("bar", "0.1.0", ["hoho~=1.1"])
    repository.add_candidate("hoho", "2.1")
    repository.add_candidate("hoho", "1.5")

    result = resolve_requirements(repository, ["foo", "bar"])
    assert result["foo"].version == "0.1.0"
    assert result["bar"].version == "0.1.0"
    assert result["hoho"].version == "1.5"


F
frostming 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166
def test_resolve_conflicting_dependencies(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["hoho>=2.0"])
    repository.add_candidate("bar", "0.1.0")
    repository.add_dependencies("bar", "0.1.0", ["hoho~=1.1"])
    repository.add_candidate("hoho", "2.1")
    repository.add_candidate("hoho", "1.5")
    with pytest.raises(ResolutionImpossible):
        resolve_requirements(repository, ["foo", "bar"])


def test_resolve_no_available_versions(project, repository):
    repository.add_candidate("foo", "0.1.0")
F
frostming 已提交
167
    with pytest.raises(ResolutionImpossible):
F
frostming 已提交
168 169 170
        resolve_requirements(repository, ["foo>=0.2.0"])


F
frostming 已提交
171 172 173 174 175
def test_exclude_incompatible_requirements(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["bar; python_version < '3'"])
    result = resolve_requirements(repository, ["foo"], ">=3.6")
    assert "bar" not in result
F
frostming 已提交
176 177


178 179 180 181 182 183 184 185
def test_union_markers_from_different_parents(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["bar; python_version < '3'"])
    repository.add_candidate("bar", "0.1.0")
    result = resolve_requirements(repository, ["foo", "bar"], ">=3.6")
    assert not result["bar"].requires_python


F
frostming 已提交
186 187 188
def test_requirements_from_different_sections(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_candidate("foo", "0.2.0")
F
Frost Ming 已提交
189
    requirements = ["foo", "foo<0.2.0"]
F
frostming 已提交
190 191
    result = resolve_requirements(repository, requirements)
    assert result["foo"].version == "0.1.0"
F
Frost Ming 已提交
192 193 194 195 196 197 198 199


def test_resolve_two_extras_from_the_same_package(project, repository):
    # Case borrowed from pypa/pip#7096
    line = (FIXTURES / "projects/demo_extras").as_posix() + "[extra1,extra2]"
    result = resolve_requirements(repository, [line])
    assert "pysocks" in result
    assert "pyopenssl" in result
F
Frost Ming 已提交
200 201 202


def test_resolve_package_with_dummy_upbound(project, repository):
F
frostming 已提交
203
    repository.add_candidate("foo", "0.1.0", ">=3.5,<4.0")
F
Frost Ming 已提交
204 205
    result = resolve_requirements(repository, ["foo"], ">=3.5")
    assert "foo" in result
F
frostming 已提交
206 207 208 209 210 211 212 213 214 215


def test_resolve_dependency_with_extra_marker(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["pytz; extra=='tz' or extra=='all'"])
    result = resolve_requirements(repository, ["foo"])
    assert "pytz" not in result

    result = resolve_requirements(repository, ["foo[tz]"])
    assert "pytz" in result
F
Frost Ming 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236


def test_resolve_parent_from_multiple_sources(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["django"])
    repository.add_candidate("bar", "0.1.0")
    repository.add_dependencies("bar", "0.1.0", ["django"])
    result = resolve_requirements(
        repository, ["foo; python_version ~= '3.8'", "bar"], ">=3.6"
    )
    assert not result["pytz"].marker


def test_resolve_circular_dependencies(project, repository):
    repository.add_candidate("foo", "0.1.0")
    repository.add_dependencies("foo", "0.1.0", ["foobar"])
    repository.add_candidate("foobar", "0.2.0")
    repository.add_dependencies("foobar", "0.2.0", ["foo"])
    result = resolve_requirements(repository, ["foo"])
    assert result["foo"].version == "0.1.0"
    assert result["foobar"].version == "0.2.0"