poetry/tests/puzzle/test_provider.py

882 lines
30 KiB
Python

from __future__ import annotations
from subprocess import CalledProcessError
from typing import TYPE_CHECKING
from typing import Any
import pytest
from cleo.io.null_io import NullIO
from packaging.utils import canonicalize_name
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.directory_dependency import DirectoryDependency
from poetry.core.packages.file_dependency import FileDependency
from poetry.core.packages.package import Package
from poetry.core.packages.project_package import ProjectPackage
from poetry.core.packages.url_dependency import URLDependency
from poetry.core.packages.vcs_dependency import VCSDependency
from poetry.factory import Factory
from poetry.inspection.info import PackageInfo
from poetry.packages import DependencyPackage
from poetry.puzzle.provider import IncompatibleConstraintsError
from poetry.puzzle.provider import Provider
from poetry.repositories.exceptions import PackageNotFound
from poetry.repositories.repository import Repository
from poetry.repositories.repository_pool import Priority
from poetry.repositories.repository_pool import RepositoryPool
from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
from tests.helpers import get_dependency
if TYPE_CHECKING:
from pytest_mock import MockerFixture
from tests.types import FixtureDirGetter
SOME_URL = "https://example.com/path.tar.gz"
class MockEnv(BaseMockEnv):
def run(self, bin: str, *args: str, **kwargs: Any) -> str:
raise EnvCommandError(CalledProcessError(1, "python", output=""))
@pytest.fixture
def root() -> ProjectPackage:
return ProjectPackage("root", "1.2.3")
@pytest.fixture
def repository() -> Repository:
return Repository("repo")
@pytest.fixture
def pool(repository: Repository) -> RepositoryPool:
pool = RepositoryPool()
pool.add_repository(repository)
return pool
@pytest.fixture
def provider(root: ProjectPackage, pool: RepositoryPool) -> Provider:
return Provider(root, pool, NullIO())
@pytest.mark.parametrize(
"dependency, expected",
[
(Dependency("foo", "<2"), [Package("foo", "1")]),
(Dependency("foo", "<2", extras=["bar"]), [Package("foo", "1")]),
(Dependency("foo", ">=1"), [Package("foo", "2"), Package("foo", "1")]),
(Dependency("foo", ">=1a"), [Package("foo", "2"), Package("foo", "1")]),
(
Dependency("foo", ">=1", allows_prereleases=True),
[
Package("foo", "3a"),
Package("foo", "2"),
Package("foo", "2a"),
Package("foo", "1"),
],
),
],
)
def test_search_for(
provider: Provider,
repository: Repository,
dependency: Dependency,
expected: list[Package],
) -> None:
foo1 = Package("foo", "1")
foo2a = Package("foo", "2a")
foo2 = Package("foo", "2")
foo3a = Package("foo", "3a")
repository.add_package(foo1)
repository.add_package(foo2a)
repository.add_package(foo2)
repository.add_package(foo3a)
assert provider.search_for(dependency) == expected
@pytest.mark.parametrize(
"dependency, direct_origin_dependency, expected_before, expected_after",
[
(
Dependency("foo", ">=1"),
URLDependency("foo", SOME_URL),
[Package("foo", "3")],
[Package("foo", "2a", source_type="url", source_url=SOME_URL)],
),
(
Dependency("foo", ">=2"),
URLDependency("foo", SOME_URL),
[Package("foo", "3")],
[Package("foo", "3")],
),
(
Dependency("foo", ">=1", extras=["bar"]),
URLDependency("foo", SOME_URL),
[Package("foo", "3")],
[Package("foo", "2a", source_type="url", source_url=SOME_URL)],
),
(
Dependency("foo", ">=1"),
URLDependency("foo", SOME_URL, extras=["baz"]),
[Package("foo", "3")],
[Package("foo", "2a", source_type="url", source_url=SOME_URL)],
),
(
Dependency("foo", ">=1", extras=["bar"]),
URLDependency("foo", SOME_URL, extras=["baz"]),
[Package("foo", "3")],
[Package("foo", "2a", source_type="url", source_url=SOME_URL)],
),
],
)
def test_search_for_direct_origin_and_extras(
provider: Provider,
repository: Repository,
mocker: MockerFixture,
dependency: Dependency,
direct_origin_dependency: Dependency,
expected_before: list[Package],
expected_after: list[Package],
) -> None:
foo2a_direct_origin = Package("foo", "2a", source_type="url", source_url=SOME_URL)
mocker.patch(
"poetry.puzzle.provider.Provider.search_for_direct_origin_dependency",
return_value=foo2a_direct_origin,
)
foo2a = Package("foo", "2a")
foo3 = Package("foo", "3")
repository.add_package(foo2a)
repository.add_package(foo3)
assert provider.search_for(dependency) == expected_before
assert provider.search_for(direct_origin_dependency) == [foo2a_direct_origin]
assert provider.search_for(dependency) == expected_after
@pytest.mark.parametrize("value", [True, False])
def test_search_for_vcs_retains_develop_flag(provider: Provider, value: bool) -> None:
dependency = VCSDependency(
"demo", "git", "https://github.com/demo/demo.git", develop=value
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.develop == value
def test_search_for_vcs_setup_egg_info(provider: Provider) -> None:
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_setup_egg_info_with_extras(provider: Provider) -> None:
dependency = VCSDependency(
"demo", "git", "https://github.com/demo/demo.git", extras=["foo"]
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_read_setup(provider: Provider, mocker: MockerFixture) -> None:
mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_read_setup_with_extras(
provider: Provider, mocker: MockerFixture
) -> None:
mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv())
dependency = VCSDependency(
"demo", "git", "https://github.com/demo/demo.git", extras=["foo"]
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
def test_search_for_vcs_read_setup_raises_error_if_no_version(
provider: Provider, mocker: MockerFixture
) -> None:
mocker.patch(
"poetry.inspection.info.get_pep517_metadata",
return_value=PackageInfo(name="demo", version=None),
)
dependency = VCSDependency("demo", "git", "https://github.com/demo/no-version.git")
with pytest.raises(RuntimeError):
provider.search_for_direct_origin_dependency(dependency)
@pytest.mark.parametrize("directory", ["demo", "non-canonical-name"])
def test_search_for_directory_setup_egg_info(
provider: Provider, directory: str, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / directory,
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_setup_egg_info_with_extras(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / "demo",
extras=["foo"],
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.parametrize("directory", ["demo", "non-canonical-name"])
def test_search_for_directory_setup_with_base(
provider: Provider, directory: str, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / directory,
base=fixture_dir("git") / "github.com" / "demo" / directory,
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
assert package.root_dir == (fixture_dir("git") / "github.com" / "demo" / directory)
def test_search_for_directory_setup_read_setup(
provider: Provider, mocker: MockerFixture, fixture_dir: FixtureDirGetter
) -> None:
mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / "demo",
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_setup_read_setup_with_extras(
provider: Provider, mocker: MockerFixture, fixture_dir: FixtureDirGetter
) -> None:
mocker.patch("poetry.utils.env.EnvManager.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / "demo",
extras=["foo"],
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
required = [r for r in package.requires if not r.is_optional()]
optional = [r for r in package.requires if r.is_optional()]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [get_dependency("tomlkit"), get_dependency("cleo")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_setup_read_setup_with_no_dependencies(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"demo",
fixture_dir("git") / "github.com" / "demo" / "no-dependencies",
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == []
assert package.extras == {}
def test_search_for_directory_poetry(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"project-with-extras",
fixture_dir("project_with_extras"),
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == []
assert optional == [
get_dependency("cachy", ">=0.2.0"),
get_dependency("pendulum", ">=1.4.4"),
]
extras_a = canonicalize_name("extras-a")
extras_b = canonicalize_name("extras-b")
assert set(package.extras) == {extras_a, extras_b}
assert set(package.extras[extras_a]) == {get_dependency("pendulum", ">=1.4.4")}
assert set(package.extras[extras_b]) == {get_dependency("cachy", ">=0.2.0")}
def test_search_for_directory_poetry_with_extras(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = DirectoryDependency(
"project-with-extras",
fixture_dir("project_with_extras"),
extras=["extras_a"],
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == []
assert optional == [
get_dependency("cachy", ">=0.2.0"),
get_dependency("pendulum", ">=1.4.4"),
]
extras_a = canonicalize_name("extras-a")
extras_b = canonicalize_name("extras-b")
assert set(package.extras) == {extras_a, extras_b}
assert set(package.extras[extras_a]) == {get_dependency("pendulum", ">=1.4.4")}
assert set(package.extras[extras_b]) == {get_dependency("cachy", ">=0.2.0")}
def test_search_for_file_sdist(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = FileDependency(
"demo",
fixture_dir("distributions") / "demo-0.1.0.tar.gz",
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.0"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [
get_dependency("cleo"),
get_dependency("tomlkit"),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_sdist_with_extras(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = FileDependency(
"demo",
fixture_dir("distributions") / "demo-0.1.0.tar.gz",
extras=["foo"],
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.0"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [
get_dependency("cleo"),
get_dependency("tomlkit"),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = FileDependency(
"demo",
fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl",
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.0"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [
get_dependency("cleo"),
get_dependency("tomlkit"),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel_with_extras(
provider: Provider, fixture_dir: FixtureDirGetter
) -> None:
dependency = FileDependency(
"demo",
fixture_dir("distributions") / "demo-0.1.0-py2.py3-none-any.whl",
extras=["foo"],
)
package = provider.search_for_direct_origin_dependency(dependency)
assert package.name == "demo"
assert package.version.text == "0.1.0"
required = [
r for r in sorted(package.requires, key=lambda r: r.name) if not r.is_optional()
]
optional = [
r for r in sorted(package.requires, key=lambda r: r.name) if r.is_optional()
]
assert required == [get_dependency("pendulum", ">=1.4.4")]
assert optional == [
get_dependency("cleo"),
get_dependency("tomlkit"),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_complete_package_merges_same_source_and_no_source(
provider: Provider, root: ProjectPackage
) -> None:
foo_no_source_1 = get_dependency("foo", ">=1")
foo_source_1 = get_dependency("foo", "!=1.1.*")
foo_source_1.source_name = "source"
foo_source_2 = get_dependency("foo", "!=1.2.*")
foo_source_2.source_name = "source"
foo_no_source_2 = get_dependency("foo", "<2")
root.add_dependency(foo_no_source_1)
root.add_dependency(foo_source_1)
root.add_dependency(foo_source_2)
root.add_dependency(foo_no_source_2)
complete_package = provider.complete_package(
DependencyPackage(root.to_dependency(), root)
)
requires = complete_package.package.all_requires
assert len(requires) == 1
assert requires[0].source_name == "source"
assert str(requires[0].constraint) in {
">=1,<1.1 || >=1.3,<2",
">=1,<1.1.dev0 || >=1.3.dev0,<2",
">=1,<1.1.0 || >=1.3.0,<2",
">=1,<1.1.0.dev0 || >=1.3.0.dev0,<2",
}
def test_complete_package_does_not_merge_different_source_names(
provider: Provider, root: ProjectPackage
) -> None:
foo_source_1 = get_dependency("foo")
foo_source_1.source_name = "source_1"
foo_source_2 = get_dependency("foo")
foo_source_2.source_name = "source_2"
root.add_dependency(foo_source_1)
root.add_dependency(foo_source_2)
with pytest.raises(IncompatibleConstraintsError) as e:
provider.complete_package(DependencyPackage(root.to_dependency(), root))
expected = """\
Incompatible constraints in requirements of root (1.2.3):
foo ; source=source_2
foo ; source=source_1"""
assert str(e.value) == expected
def test_complete_package_merges_same_source_type_and_no_source(
provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter
) -> None:
project_dir = fixture_dir("with_conditional_path_deps")
path = (project_dir / "demo_one").as_posix()
root.add_dependency(Factory.create_dependency("demo", ">=1.0"))
root.add_dependency(Factory.create_dependency("demo", {"path": path}))
root.add_dependency(Factory.create_dependency("demo", {"path": path})) # duplicate
root.add_dependency(Factory.create_dependency("demo", "<2.0"))
complete_package = provider.complete_package(
DependencyPackage(root.to_dependency(), root)
)
requires = complete_package.package.all_requires
assert len(requires) == 1
assert requires[0].source_url == path
assert str(requires[0].constraint) == "1.2.3"
def test_complete_package_does_not_merge_different_source_types(
provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter
) -> None:
project_dir = fixture_dir("with_conditional_path_deps")
for folder in ["demo_one", "demo_two"]:
path = (project_dir / folder).as_posix()
root.add_dependency(Factory.create_dependency("demo", {"path": path}))
with pytest.raises(IncompatibleConstraintsError) as e:
provider.complete_package(DependencyPackage(root.to_dependency(), root))
expected = f"""\
Incompatible constraints in requirements of root (1.2.3):
demo @ {project_dir.as_uri()}/demo_two (1.2.3)
demo @ {project_dir.as_uri()}/demo_one (1.2.3)"""
assert str(e.value) == expected
def test_complete_package_does_not_merge_different_source_type_and_name(
provider: Provider, root: ProjectPackage, fixture_dir: FixtureDirGetter
) -> None:
project_dir = fixture_dir("with_conditional_path_deps")
path = (project_dir / "demo_one").as_posix()
dep_with_source_name = Factory.create_dependency("demo", ">=1.0")
dep_with_source_name.source_name = "source"
root.add_dependency(dep_with_source_name)
root.add_dependency(Factory.create_dependency("demo", {"path": path}))
with pytest.raises(IncompatibleConstraintsError) as e:
provider.complete_package(DependencyPackage(root.to_dependency(), root))
expected = f"""\
Incompatible constraints in requirements of root (1.2.3):
demo @ {project_dir.as_uri()}/demo_one (1.2.3)
demo (>=1.0) ; source=source"""
assert str(e.value) == expected
def test_complete_package_does_not_merge_different_subdirectories(
provider: Provider, root: ProjectPackage
) -> None:
dependency_one = Factory.create_dependency(
"one",
{
"git": "https://github.com/demo/subdirectories.git",
"subdirectory": "one",
},
)
dependency_one_copy = Factory.create_dependency(
"one",
{
"git": "https://github.com/demo/subdirectories.git",
"subdirectory": "one-copy",
},
)
root.add_dependency(dependency_one)
root.add_dependency(dependency_one_copy)
with pytest.raises(IncompatibleConstraintsError) as e:
provider.complete_package(DependencyPackage(root.to_dependency(), root))
expected = """\
Incompatible constraints in requirements of root (1.2.3):
one @ git+https://github.com/demo/subdirectories.git#subdirectory=one-copy (1.0.0)
one @ git+https://github.com/demo/subdirectories.git#subdirectory=one (1.0.0)"""
assert str(e.value) == expected
@pytest.mark.parametrize("source_name", [None, "repo"])
def test_complete_package_with_extras_preserves_source_name(
provider: Provider, repository: Repository, source_name: str | None
) -> None:
package_a = Package("A", "1.0")
package_b = Package("B", "1.0")
dep = get_dependency("B", "^1.0", optional=True)
package_a.add_dependency(dep)
package_a.extras = {canonicalize_name("foo"): [dep]}
repository.add_package(package_a)
repository.add_package(package_b)
dependency = Dependency("A", "1.0", extras=["foo"])
if source_name:
dependency.source_name = source_name
complete_package = provider.complete_package(
DependencyPackage(dependency, package_a)
)
requires = complete_package.package.all_requires
assert len(requires) == 2
assert requires[0].name == "a"
assert requires[0].source_name == source_name
assert requires[1].name == "b"
assert requires[1].source_name is None
@pytest.mark.parametrize("with_extra", [False, True])
def test_complete_package_fetches_optional_vcs_dependency_only_if_requested(
provider: Provider, repository: Repository, mocker: MockerFixture, with_extra: bool
) -> None:
optional_vcs_dependency = Factory.create_dependency(
"demo", {"git": "https://github.com/demo/demo.git", "optional": True}
)
package = Package("A", "1.0", features=["foo"] if with_extra else [])
package.add_dependency(optional_vcs_dependency)
package.extras = {canonicalize_name("foo"): [optional_vcs_dependency]}
repository.add_package(package)
spy = mocker.spy(provider, "_search_for_vcs")
provider.complete_package(DependencyPackage(package.to_dependency(), package))
if with_extra:
spy.assert_called()
else:
spy.assert_not_called()
def test_complete_package_finds_locked_package_in_explicit_source(
root: ProjectPackage, pool: RepositoryPool
) -> None:
package = Package("a", "1.0", source_reference="explicit")
explicit_repo = Repository("explicit")
explicit_repo.add_package(package)
pool.add_repository(explicit_repo, priority=Priority.EXPLICIT)
root_dependency = get_dependency("a", ">0")
root_dependency.source_name = "explicit"
root.add_dependency(root_dependency)
locked_package = Package("a", "1.0", source_reference="explicit")
provider = Provider(root, pool, NullIO(), locked=[locked_package])
provider.complete_package(DependencyPackage(root.to_dependency(), root))
# transitive dependency without explicit source
dependency = get_dependency("a", ">=1")
locked = provider.get_locked(dependency)
assert locked is not None
provider.complete_package(locked) # must not fail
def test_complete_package_finds_locked_package_in_other_source(
root: ProjectPackage, repository: Repository, pool: RepositoryPool
) -> None:
package = Package("a", "1.0")
repository.add_package(package)
explicit_repo = Repository("explicit")
pool.add_repository(explicit_repo)
root_dependency = get_dependency("a", ">0") # no explicit source
root.add_dependency(root_dependency)
locked_package = Package("a", "1.0", source_reference="explicit") # explicit source
provider = Provider(root, pool, NullIO(), locked=[locked_package])
provider.complete_package(DependencyPackage(root.to_dependency(), root))
# transitive dependency without explicit source
dependency = get_dependency("a", ">=1")
locked = provider.get_locked(dependency)
assert locked is not None
provider.complete_package(locked) # must not fail
def test_complete_package_raises_packagenotfound_if_locked_source_not_available(
root: ProjectPackage, pool: RepositoryPool, provider: Provider
) -> None:
locked_package = Package("a", "1.0", source_reference="outdated")
provider = Provider(root, pool, NullIO(), locked=[locked_package])
provider.complete_package(DependencyPackage(root.to_dependency(), root))
# transitive dependency without explicit source
dependency = get_dependency("a", ">=1")
locked = provider.get_locked(dependency)
assert locked is not None
with pytest.raises(PackageNotFound):
provider.complete_package(locked)
def test_source_dependency_is_satisfied_by_direct_origin(
provider: Provider, repository: Repository
) -> None:
direct_origin_package = Package("foo", "1.1", source_type="url")
repository.add_package(Package("foo", "1.0"))
provider._direct_origin_packages = {"foo": direct_origin_package}
dep = Dependency("foo", ">=1")
assert provider.search_for(dep) == [direct_origin_package]
def test_explicit_source_dependency_is_not_satisfied_by_direct_origin(
provider: Provider, repository: Repository
) -> None:
repo_package = Package("foo", "1.0")
repository.add_package(repo_package)
provider._direct_origin_packages = {"foo": Package("foo", "1.1", source_type="url")}
dep = Dependency("foo", ">=1")
dep.source_name = repository.name
assert provider.search_for(dep) == [repo_package]
def test_source_dependency_is_not_satisfied_by_incompatible_direct_origin(
provider: Provider, repository: Repository
) -> None:
repo_package = Package("foo", "2.0")
repository.add_package(repo_package)
provider._direct_origin_packages = {"foo": Package("foo", "1.0", source_type="url")}
dep = Dependency("foo", ">=2")
dep.source_name = repository.name
assert provider.search_for(dep) == [repo_package]