Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make allow-prereleases a tri-state setting to really forbid pre-releases if the setting is false and keep default behavior to allow pre-releases only if necessary #9798

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/dependency-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,3 +554,17 @@ markers = "platform_python_implementation == 'CPython'"
The same information is still present, and ends up providing the exact
same specification. It's simply split into multiple, slightly more readable,
lines.

### Handling of pre-releases

Per default, Poetry will prefer stable releases and only choose a pre-release
if no stable release satisfies a version constraint. In some cases, this may result in
a solution containing pre-releases even if another solution without pre-releases exists.

If you want to disallow pre-releases for a specific dependency,
you can set `allow-prereleases` to `false`. In this case, dependency resolution will
fail if there is no solution without choosing a pre-release.

If you want to prefer the latest version of a dependency even if it is a pre-release,
you can set `allow-prereleases` to `true` so that Poetry makes no distinction
between stable and pre-release versions during dependency resolution.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/poetry/console/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def handle(self) -> int:

requirements = self._determine_requirements(
packages,
allow_prereleases=self.option("allow-prereleases"),
allow_prereleases=self.option("allow-prereleases") or None,
source=self.option("source"),
)

Expand Down
4 changes: 2 additions & 2 deletions src/poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def _generate_choice_list(
def _determine_requirements(
self,
requires: list[str],
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
is_interactive: bool | None = None,
) -> list[dict[str, Any]]:
Expand Down Expand Up @@ -440,7 +440,7 @@ def _find_best_version_for_package(
self,
name: str,
required_version: str | None = None,
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
) -> tuple[str, str]:
from poetry.version.version_selector import VersionSelector
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/console/commands/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ def find_latest_package(
provider = Provider(root, self.poetry.pool, NullIO())
return provider.search_for_direct_origin_dependency(dep)

allow_prereleases = False
allow_prereleases: bool | None = None
for dep in requires:
if dep.name == package.name:
allow_prereleases = dep.allows_prereleases()
Expand Down
2 changes: 2 additions & 0 deletions src/poetry/repositories/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def find_packages(self, dependency: Dependency) -> list[Package]:
level="debug",
)

if allow_prereleases is False: # in contrast to None!
return packages
return packages or ignored_pre_release_packages

def has_package(self, package: Package) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion src/poetry/version/version_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def find_best_candidate(
self,
package_name: str,
target_package_version: str | None = None,
allow_prereleases: bool = False,
allow_prereleases: bool | None = None,
source: str | None = None,
) -> Package | None:
"""
Expand Down
45 changes: 38 additions & 7 deletions tests/repositories/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,46 @@


@pytest.fixture(scope="module")
def black_repository() -> Repository:
def repository() -> Repository:
repo = Repository("repo")

# latest version pre-release
repo.add_package(get_package("foo", "1.0"))
repo.add_package(get_package("foo", "2.0.b0"))

# latest version yanked
repo.add_package(get_package("black", "19.10b0"))
repo.add_package(get_package("black", "21.11b0", yanked="reason"))

return repo


@pytest.mark.parametrize(
("allow_prereleases", "constraint", "expected"),
[
(None, ">=1.0", ["1.0"]),
(False, ">=1.0", ["1.0"]),
(True, ">=1.0", ["1.0", "2.0.b0"]),
(None, ">=1.5", ["2.0.b0"]),
(False, ">=1.5", []),
(True, ">=1.5", ["2.0.b0"]),
],
)
def test_find_packages_allow_prereleases(
repository: Repository,
allow_prereleases: bool | None,
constraint: str,
expected: list[str],
) -> None:
packages = repository.find_packages(
Factory.create_dependency(
"foo", {"version": constraint, "allow-prereleases": allow_prereleases}
)
)

assert [str(p.version) for p in packages] == expected


@pytest.mark.parametrize(
["constraint", "expected"],
[
Expand All @@ -29,11 +62,9 @@ def black_repository() -> Repository:
],
)
def test_find_packages_yanked(
black_repository: Repository, constraint: str, expected: list[str]
repository: Repository, constraint: str, expected: list[str]
) -> None:
packages = black_repository.find_packages(
Factory.create_dependency("black", constraint)
)
packages = repository.find_packages(Factory.create_dependency("black", constraint))

assert [str(p.version) for p in packages] == expected

Expand All @@ -46,13 +77,13 @@ def test_find_packages_yanked(
],
)
def test_package_yanked(
black_repository: Repository,
repository: Repository,
package_name: str,
version: str,
yanked: bool,
yanked_reason: str,
) -> None:
package = black_repository.package(package_name, Version.parse(version))
package = repository.package(package_name, Version.parse(version))

assert package.name == package_name
assert str(package.version) == version
Expand Down
Loading