Commit f0e55b0a by Randy Döring

solver: support for yanked releases according to PEP 592

parent c1d77a1a
...@@ -290,6 +290,7 @@ class Provider: ...@@ -290,6 +290,7 @@ class Provider:
packages.sort( packages.sort(
key=lambda p: ( key=lambda p: (
not p.yanked,
not p.is_prerelease() and not dependency.allows_prereleases(), not p.is_prerelease() and not dependency.allows_prereleases(),
p.version, p.version,
), ),
......
...@@ -45,8 +45,10 @@ FIXTURE_PATH = Path(__file__).parent / "fixtures" ...@@ -45,8 +45,10 @@ FIXTURE_PATH = Path(__file__).parent / "fixtures"
MOCK_DEFAULT_GIT_REVISION = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" MOCK_DEFAULT_GIT_REVISION = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
def get_package(name: str, version: str | Version) -> Package: def get_package(
return Package(name, version) name: str, version: str | Version, yanked: str | bool = False
) -> Package:
return Package(name, version, yanked=yanked)
def get_dependency( def get_dependency(
......
...@@ -13,6 +13,7 @@ from poetry.packages import DependencyPackage ...@@ -13,6 +13,7 @@ from poetry.packages import DependencyPackage
if TYPE_CHECKING: if TYPE_CHECKING:
from poetry.core.packages.project_package import ProjectPackage from poetry.core.packages.project_package import ProjectPackage
from poetry.mixology import SolverResult
from poetry.repositories import Repository from poetry.repositories import Repository
from tests.mixology.version_solver.conftest import Provider from tests.mixology.version_solver.conftest import Provider
...@@ -23,8 +24,9 @@ def add_to_repo( ...@@ -23,8 +24,9 @@ def add_to_repo(
version: str, version: str,
deps: dict[str, str] | None = None, deps: dict[str, str] | None = None,
python: str | None = None, python: str | None = None,
yanked: bool = False,
) -> None: ) -> None:
package = Package(name, version) package = Package(name, version, yanked=yanked)
if python: if python:
package.python_versions = python package.python_versions = python
...@@ -43,7 +45,7 @@ def check_solver_result( ...@@ -43,7 +45,7 @@ def check_solver_result(
tries: int | None = None, tries: int | None = None,
locked: dict[str, Package] | None = None, locked: dict[str, Package] | None = None,
use_latest: list[str] | None = None, use_latest: list[str] | None = None,
) -> None: ) -> SolverResult | None:
if locked is not None: if locked is not None:
locked = { locked = {
k: [DependencyPackage(l.to_dependency(), l)] for k, l in locked.items() k: [DependencyPackage(l.to_dependency(), l)] for k, l in locked.items()
...@@ -59,20 +61,22 @@ def check_solver_result( ...@@ -59,20 +61,22 @@ def check_solver_result(
if tries is not None: if tries is not None:
assert solver.solution.attempted_solutions == tries assert solver.solution.attempted_solutions == tries
return return None
raise raise
except AssertionError as e: except AssertionError as e:
if error: if error:
assert str(e) == error assert str(e) == error
return return None
raise raise
packages = {} packages = {}
for package in solution.packages: for package in solution.packages:
packages[package.name] = str(package.version) packages[package.name] = str(package.version)
assert result == packages assert packages == result
if tries is not None: if tries is not None:
assert solution.attempted_solutions == tries assert solution.attempted_solutions == tries
return solution
...@@ -2,6 +2,8 @@ from __future__ import annotations ...@@ -2,6 +2,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest
from poetry.factory import Factory from poetry.factory import Factory
from tests.mixology.helpers import add_to_repo from tests.mixology.helpers import add_to_repo
from tests.mixology.helpers import check_solver_result from tests.mixology.helpers import check_solver_result
...@@ -87,3 +89,44 @@ def test_circular_dependency( ...@@ -87,3 +89,44 @@ def test_circular_dependency(
add_to_repo(repo, "bar", "1.0.0", deps={"foo": "1.0.0"}) add_to_repo(repo, "bar", "1.0.0", deps={"foo": "1.0.0"})
check_solver_result(root, provider, {"foo": "1.0.0", "bar": "1.0.0"}) check_solver_result(root, provider, {"foo": "1.0.0", "bar": "1.0.0"})
@pytest.mark.parametrize(
"constraint, versions, yanked_versions, expected",
[
(">=1", ["1", "2"], [], "2"),
(">=1", ["1", "2"], ["2"], "1"),
(">=1", ["1", "2", "3"], ["2"], "3"),
(">=1", ["1", "2", "3"], ["2", "3"], "1"),
(">1", ["1", "2"], ["2"], "error"),
(">1", ["2"], ["2"], "error"),
(">=2", ["2"], ["2"], "error"),
("==2", ["2"], ["2"], "2"),
("==2", ["2", "2+local"], [], "2+local"),
("==2", ["2", "2+local"], ["2+local"], "2"),
],
)
def test_yanked_release(
root: ProjectPackage,
provider: Provider,
repo: Repository,
constraint: str,
versions: list[str],
yanked_versions: list[str],
expected: str,
) -> None:
root.add_dependency(Factory.create_dependency("foo", constraint))
for version in versions:
add_to_repo(repo, "foo", version, yanked=version in yanked_versions)
if expected == "error":
result = None
error = (
f"Because myapp depends on foo ({constraint}) which doesn't match any "
"versions, version solving failed."
)
else:
result = {"foo": expected}
error = None
check_solver_result(root, provider, result, error)
...@@ -169,3 +169,31 @@ def test_with_compatible_locked_dependencies_with_extras( ...@@ -169,3 +169,31 @@ def test_with_compatible_locked_dependencies_with_extras(
"baz": get_package("baz", "1.0.0"), "baz": get_package("baz", "1.0.0"),
}, },
) )
def test_with_yanked_package_in_lock(
root: ProjectPackage, provider: Provider, repo: Repository
):
root.add_dependency(Factory.create_dependency("foo", "*"))
add_to_repo(repo, "foo", "1")
add_to_repo(repo, "foo", "2", yanked=True)
# yanked version is kept in lock file
locked_foo = get_package("foo", "2")
assert not locked_foo.yanked
result = check_solver_result(
root,
provider,
result={"foo": "2"},
locked={"foo": locked_foo},
)
foo = result.packages[0]
assert foo.yanked
# without considering the lock file, the other version is chosen
check_solver_result(
root,
provider,
result={"foo": "1"},
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment